Salome HOME
23491: EDF 15591 - Duplicate Elements / Nodes
[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 "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
102
103 using namespace std;
104 using namespace SMESH::Controls;
105
106 namespace
107 {
108   template < class ELEM_SET >
109   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
110   {
111     typedef SMDS_SetIterator
112       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
114   }
115 }
116
117 //=======================================================================
118 //function : SMESH_MeshEditor
119 //purpose  :
120 //=======================================================================
121
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123   :myMesh( theMesh ) // theMesh may be NULL
124 {
125 }
126
127 //================================================================================
128 /*!
129  * \brief Return mesh DS
130  */
131 //================================================================================
132
133 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
134 {
135   return myMesh->GetMeshDS();
136 }
137
138
139 //================================================================================
140 /*!
141  * \brief Clears myLastCreatedNodes and myLastCreatedElems
142  */
143 //================================================================================
144
145 void SMESH_MeshEditor::ClearLastCreated()
146 {
147   myLastCreatedNodes.Clear();
148   myLastCreatedElems.Clear();
149 }
150
151 //================================================================================
152 /*!
153  * \brief Initializes members by an existing element
154  *  \param [in] elem - the source element
155  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
156  */
157 //================================================================================
158
159 SMESH_MeshEditor::ElemFeatures&
160 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
161 {
162   if ( elem )
163   {
164     myType = elem->GetType();
165     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
166     {
167       myIsPoly = elem->IsPoly();
168       if ( myIsPoly )
169       {
170         myIsQuad = elem->IsQuadratic();
171         if ( myType == SMDSAbs_Volume && !basicOnly )
172         {
173           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
174           myPolyhedQuantities.swap( quant );
175         }
176       }
177     }
178     else if ( myType == SMDSAbs_Ball && !basicOnly )
179     {
180       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
181     }
182   }
183   return *this;
184 }
185
186 //=======================================================================
187 /*!
188  * \brief Add element
189  */
190 //=======================================================================
191
192 SMDS_MeshElement*
193 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
194                              const ElemFeatures&                  features)
195 {
196   SMDS_MeshElement* e = 0;
197   int nbnode = node.size();
198   SMESHDS_Mesh* mesh = GetMeshDS();
199   const int ID = features.myID;
200
201   switch ( features.myType ) {
202   case SMDSAbs_Face:
203     if ( !features.myIsPoly ) {
204       if      (nbnode == 3) {
205         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
206         else           e = mesh->AddFace      (node[0], node[1], node[2] );
207       }
208       else if (nbnode == 4) {
209         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
210         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
211       }
212       else if (nbnode == 6) {
213         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
214                                                node[4], node[5], ID);
215         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
216                                                node[4], node[5] );
217       }
218       else if (nbnode == 7) {
219         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
220                                                node[4], node[5], node[6], ID);
221         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
222                                                node[4], node[5], node[6] );
223       }
224       else if (nbnode == 8) {
225         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
226                                                node[4], node[5], node[6], node[7], ID);
227         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
228                                                node[4], node[5], node[6], node[7] );
229       }
230       else if (nbnode == 9) {
231         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
232                                                node[4], node[5], node[6], node[7], node[8], ID);
233         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
234                                                node[4], node[5], node[6], node[7], node[8] );
235       }
236     }
237     else if ( !features.myIsQuad )
238     {
239       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
240       else           e = mesh->AddPolygonalFace      (node    );
241     }
242     else if ( nbnode % 2 == 0 ) // just a protection
243     {
244       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
245       else           e = mesh->AddQuadPolygonalFace      (node    );
246     }
247     break;
248
249   case SMDSAbs_Volume:
250     if ( !features.myIsPoly ) {
251       if      (nbnode == 4) {
252         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
254       }
255       else if (nbnode == 5) {
256         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
257                                                  node[4], ID);
258         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
259                                                  node[4] );
260       }
261       else if (nbnode == 6) {
262         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
263                                                  node[4], node[5], ID);
264         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
265                                                  node[4], node[5] );
266       }
267       else if (nbnode == 8) {
268         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
269                                                  node[4], node[5], node[6], node[7], ID);
270         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
271                                                  node[4], node[5], node[6], node[7] );
272       }
273       else if (nbnode == 10) {
274         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7],
276                                                  node[8], node[9], ID);
277         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
278                                                  node[4], node[5], node[6], node[7],
279                                                  node[8], node[9] );
280       }
281       else if (nbnode == 12) {
282         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283                                                  node[4], node[5], node[6], node[7],
284                                                  node[8], node[9], node[10], node[11], ID);
285         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
286                                                  node[4], node[5], node[6], node[7],
287                                                  node[8], node[9], node[10], node[11] );
288       }
289       else if (nbnode == 13) {
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],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] );
298       }
299       else if (nbnode == 15) {
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],ID);
304         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
305                                                  node[4], node[5], node[6], node[7],
306                                                  node[8], node[9], node[10],node[11],
307                                                  node[12],node[13],node[14] );
308       }
309       else if (nbnode == 20) {
310         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
311                                                  node[4], node[5], node[6], node[7],
312                                                  node[8], node[9], node[10],node[11],
313                                                  node[12],node[13],node[14],node[15],
314                                                  node[16],node[17],node[18],node[19],ID);
315         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
316                                                  node[4], node[5], node[6], node[7],
317                                                  node[8], node[9], node[10],node[11],
318                                                  node[12],node[13],node[14],node[15],
319                                                  node[16],node[17],node[18],node[19] );
320       }
321       else if (nbnode == 27) {
322         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
323                                                  node[4], node[5], node[6], node[7],
324                                                  node[8], node[9], node[10],node[11],
325                                                  node[12],node[13],node[14],node[15],
326                                                  node[16],node[17],node[18],node[19],
327                                                  node[20],node[21],node[22],node[23],
328                                                  node[24],node[25],node[26], ID);
329         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
330                                                  node[4], node[5], node[6], node[7],
331                                                  node[8], node[9], node[10],node[11],
332                                                  node[12],node[13],node[14],node[15],
333                                                  node[16],node[17],node[18],node[19],
334                                                  node[20],node[21],node[22],node[23],
335                                                  node[24],node[25],node[26] );
336       }
337     }
338     else if ( !features.myIsQuad )
339     {
340       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
341       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
342     }
343     else
344     {
345       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
346       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
347     }
348     break;
349
350   case SMDSAbs_Edge:
351     if ( nbnode == 2 ) {
352       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
353       else           e = mesh->AddEdge      (node[0], node[1] );
354     }
355     else if ( nbnode == 3 ) {
356       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
357       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
358     }
359     break;
360
361   case SMDSAbs_0DElement:
362     if ( nbnode == 1 ) {
363       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
364       else           e = mesh->Add0DElement      (node[0] );
365     }
366     break;
367
368   case SMDSAbs_Node:
369     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
370     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
371     break;
372
373   case SMDSAbs_Ball:
374     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
375     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
376     break;
377
378   default:;
379   }
380   if ( e ) myLastCreatedElems.Append( e );
381   return e;
382 }
383
384 //=======================================================================
385 /*!
386  * \brief Add element
387  */
388 //=======================================================================
389
390 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
391                                                const ElemFeatures& features)
392 {
393   vector<const SMDS_MeshNode*> nodes;
394   nodes.reserve( nodeIDs.size() );
395   vector<int>::const_iterator id = nodeIDs.begin();
396   while ( id != nodeIDs.end() ) {
397     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
398       nodes.push_back( node );
399     else
400       return 0;
401   }
402   return AddElement( nodes, features );
403 }
404
405 //=======================================================================
406 //function : Remove
407 //purpose  : Remove a node or an element.
408 //           Modify a compute state of sub-meshes which become empty
409 //=======================================================================
410
411 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
412                               const bool         isNodes )
413 {
414   myLastCreatedElems.Clear();
415   myLastCreatedNodes.Clear();
416
417   SMESHDS_Mesh* aMesh = GetMeshDS();
418   set< SMESH_subMesh *> smmap;
419
420   int removed = 0;
421   list<int>::const_iterator it = theIDs.begin();
422   for ( ; it != theIDs.end(); it++ ) {
423     const SMDS_MeshElement * elem;
424     if ( isNodes )
425       elem = aMesh->FindNode( *it );
426     else
427       elem = aMesh->FindElement( *it );
428     if ( !elem )
429       continue;
430
431     // Notify VERTEX sub-meshes about modification
432     if ( isNodes ) {
433       const SMDS_MeshNode* node = cast2Node( elem );
434       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
435         if ( int aShapeID = node->getshapeId() )
436           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
437             smmap.insert( sm );
438     }
439     // Find sub-meshes to notify about modification
440     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
441     //     while ( nodeIt->more() ) {
442     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
443     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
444     //       if ( aPosition.get() ) {
445     //         if ( int aShapeID = aPosition->GetShapeId() ) {
446     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
447     //             smmap.insert( sm );
448     //         }
449     //       }
450     //     }
451
452     // Do remove
453     if ( isNodes )
454       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
455     else
456       aMesh->RemoveElement( elem );
457     removed++;
458   }
459
460   // Notify sub-meshes about modification
461   if ( !smmap.empty() ) {
462     set< SMESH_subMesh *>::iterator smIt;
463     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
464       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
465   }
466
467   //   // Check if the whole mesh becomes empty
468   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
469   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
470
471   return removed;
472 }
473
474 //================================================================================
475 /*!
476  * \brief Create 0D elements on all nodes of the given object.
477  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
478  *                    the all mesh is treated
479  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
480  *  \param duplicateElements - to add one more 0D element to a node or not
481  */
482 //================================================================================
483
484 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
485                                                    TIDSortedElemSet&       all0DElems,
486                                                    const bool              duplicateElements )
487 {
488   SMDS_ElemIteratorPtr elemIt;
489   if ( elements.empty() )
490   {
491     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
492   }
493   else
494   {
495     elemIt = elemSetIterator( elements );
496   }
497
498   while ( elemIt->more() )
499   {
500     const SMDS_MeshElement* e = elemIt->next();
501     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
502     while ( nodeIt->more() )
503     {
504       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
505       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
506       if ( duplicateElements || !it0D->more() )
507       {
508         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
509         all0DElems.insert( myLastCreatedElems.Last() );
510       }
511       while ( it0D->more() )
512         all0DElems.insert( it0D->next() );
513     }
514   }
515 }
516
517 //=======================================================================
518 //function : FindShape
519 //purpose  : Return an index of the shape theElem is on
520 //           or zero if a shape not found
521 //=======================================================================
522
523 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
524 {
525   myLastCreatedElems.Clear();
526   myLastCreatedNodes.Clear();
527
528   SMESHDS_Mesh * aMesh = GetMeshDS();
529   if ( aMesh->ShapeToMesh().IsNull() )
530     return 0;
531
532   int aShapeID = theElem->getshapeId();
533   if ( aShapeID < 1 )
534     return 0;
535
536   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
537     if ( sm->Contains( theElem ))
538       return aShapeID;
539
540   if ( theElem->GetType() == SMDSAbs_Node ) {
541     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
542   }
543   else {
544     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
545   }
546
547   TopoDS_Shape aShape; // the shape a node of theElem is on
548   if ( theElem->GetType() != SMDSAbs_Node )
549   {
550     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
551     while ( nodeIt->more() ) {
552       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
553       if ((aShapeID = node->getshapeId()) > 0) {
554         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
555           if ( sm->Contains( theElem ))
556             return aShapeID;
557           if ( aShape.IsNull() )
558             aShape = aMesh->IndexToShape( aShapeID );
559         }
560       }
561     }
562   }
563
564   // None of nodes is on a proper shape,
565   // find the shape among ancestors of aShape on which a node is
566   if ( !aShape.IsNull() ) {
567     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
568     for ( ; ancIt.More(); ancIt.Next() ) {
569       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
570       if ( sm && sm->Contains( theElem ))
571         return aMesh->ShapeToIndex( ancIt.Value() );
572     }
573   }
574   else
575   {
576     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
577     while ( const SMESHDS_SubMesh* sm = smIt->next() )
578       if ( sm->Contains( theElem ))
579         return sm->GetID();
580   }
581
582   return 0;
583 }
584
585 //=======================================================================
586 //function : IsMedium
587 //purpose  :
588 //=======================================================================
589
590 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
591                                 const SMDSAbs_ElementType typeToCheck)
592 {
593   bool isMedium = false;
594   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
595   while (it->more() && !isMedium ) {
596     const SMDS_MeshElement* elem = it->next();
597     isMedium = elem->IsMediumNode(node);
598   }
599   return isMedium;
600 }
601
602 //=======================================================================
603 //function : shiftNodesQuadTria
604 //purpose  : Shift nodes in the array corresponded to quadratic triangle
605 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
606 //=======================================================================
607
608 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
609 {
610   const SMDS_MeshNode* nd1 = aNodes[0];
611   aNodes[0] = aNodes[1];
612   aNodes[1] = aNodes[2];
613   aNodes[2] = nd1;
614   const SMDS_MeshNode* nd2 = aNodes[3];
615   aNodes[3] = aNodes[4];
616   aNodes[4] = aNodes[5];
617   aNodes[5] = nd2;
618 }
619
620 //=======================================================================
621 //function : nbEdgeConnectivity
622 //purpose  : return number of the edges connected with the theNode.
623 //           if theEdges has connections with the other type of the
624 //           elements, return -1
625 //=======================================================================
626
627 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
628 {
629   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
630   // int nb=0;
631   // while(elemIt->more()) {
632   //   elemIt->next();
633   //   nb++;
634   // }
635   // return nb;
636   return theNode->NbInverseElements();
637 }
638
639 //=======================================================================
640 //function : getNodesFromTwoTria
641 //purpose  : 
642 //=======================================================================
643
644 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
645                                 const SMDS_MeshElement * theTria2,
646                                 vector< const SMDS_MeshNode*>& N1,
647                                 vector< const SMDS_MeshNode*>& N2)
648 {
649   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
650   if ( N1.size() < 6 ) return false;
651   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
652   if ( N2.size() < 6 ) return false;
653
654   int sames[3] = {-1,-1,-1};
655   int nbsames = 0;
656   int i, j;
657   for(i=0; i<3; i++) {
658     for(j=0; j<3; j++) {
659       if(N1[i]==N2[j]) {
660         sames[i] = j;
661         nbsames++;
662         break;
663       }
664     }
665   }
666   if(nbsames!=2) return false;
667   if(sames[0]>-1) {
668     shiftNodesQuadTria(N1);
669     if(sames[1]>-1) {
670       shiftNodesQuadTria(N1);
671     }
672   }
673   i = sames[0] + sames[1] + sames[2];
674   for(; i<2; i++) {
675     shiftNodesQuadTria(N2);
676   }
677   // now we receive following N1 and N2 (using numeration as in the image below)
678   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
679   // i.e. first nodes from both arrays form a new diagonal
680   return true;
681 }
682
683 //=======================================================================
684 //function : InverseDiag
685 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
686 //           but having other common link.
687 //           Return False if args are improper
688 //=======================================================================
689
690 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
691                                     const SMDS_MeshElement * theTria2 )
692 {
693   myLastCreatedElems.Clear();
694   myLastCreatedNodes.Clear();
695
696   if (!theTria1 || !theTria2)
697     return false;
698
699   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
700   if (!F1) return false;
701   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
702   if (!F2) return false;
703   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
704       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
705
706     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
707     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
708     //    |/ |                                         | \|
709     //  B +--+ 2                                     B +--+ 2
710
711     // put nodes in array and find out indices of the same ones
712     const SMDS_MeshNode* aNodes [6];
713     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
714     int i = 0;
715     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
716     while ( it->more() ) {
717       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
718
719       if ( i > 2 ) // theTria2
720         // find same node of theTria1
721         for ( int j = 0; j < 3; j++ )
722           if ( aNodes[ i ] == aNodes[ j ]) {
723             sameInd[ j ] = i;
724             sameInd[ i ] = j;
725             break;
726           }
727       // next
728       i++;
729       if ( i == 3 ) {
730         if ( it->more() )
731           return false; // theTria1 is not a triangle
732         it = theTria2->nodesIterator();
733       }
734       if ( i == 6 && it->more() )
735         return false; // theTria2 is not a triangle
736     }
737
738     // find indices of 1,2 and of A,B in theTria1
739     int iA = -1, iB = 0, i1 = 0, i2 = 0;
740     for ( i = 0; i < 6; i++ ) {
741       if ( sameInd [ i ] == -1 ) {
742         if ( i < 3 ) i1 = i;
743         else         i2 = i;
744       }
745       else if (i < 3) {
746         if ( iA >= 0) iB = i;
747         else          iA = i;
748       }
749     }
750     // nodes 1 and 2 should not be the same
751     if ( aNodes[ i1 ] == aNodes[ i2 ] )
752       return false;
753
754     // theTria1: A->2
755     aNodes[ iA ] = aNodes[ i2 ];
756     // theTria2: B->1
757     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
758
759     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
760     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
761
762     return true;
763
764   } // end if(F1 && F2)
765
766   // check case of quadratic faces
767   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
768       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
769     return false;
770   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
771       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
772     return false;
773
774   //       5
775   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
776   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
777   //    |   / |
778   //  7 +  +  + 6
779   //    | /9  |
780   //    |/    |
781   //  4 +--+--+ 3
782   //       8
783
784   vector< const SMDS_MeshNode* > N1;
785   vector< const SMDS_MeshNode* > N2;
786   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
787     return false;
788   // now we receive following N1 and N2 (using numeration as above image)
789   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
790   // i.e. first nodes from both arrays determ new diagonal
791
792   vector< const SMDS_MeshNode*> N1new( N1.size() );
793   vector< const SMDS_MeshNode*> N2new( N2.size() );
794   N1new.back() = N1.back(); // central node of biquadratic
795   N2new.back() = N2.back();
796   N1new[0] = N1[0];  N2new[0] = N1[0];
797   N1new[1] = N2[0];  N2new[1] = N1[1];
798   N1new[2] = N2[1];  N2new[2] = N2[0];
799   N1new[3] = N1[4];  N2new[3] = N1[3];
800   N1new[4] = N2[3];  N2new[4] = N2[5];
801   N1new[5] = N1[5];  N2new[5] = N1[4];
802   // change nodes in faces
803   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
804   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
805
806   // move the central node of biquadratic triangle
807   SMESH_MesherHelper helper( *GetMesh() );
808   for ( int is2nd = 0; is2nd < 2; ++is2nd )
809   {
810     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
811     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
812     if ( nodes.size() < 7 )
813       continue;
814     helper.SetSubShape( tria->getshapeId() );
815     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
816     gp_Pnt xyz;
817     if ( F.IsNull() )
818     {
819       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
820               SMESH_TNodeXYZ( nodes[4] ) +
821               SMESH_TNodeXYZ( nodes[5] )) / 3.;
822     }
823     else
824     {
825       bool checkUV;
826       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
827                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
828                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
829       TopLoc_Location loc;
830       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
831       xyz = S->Value( uv.X(), uv.Y() );
832       xyz.Transform( loc );
833       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
834            nodes[6]->getshapeId() > 0 )
835         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
836     }
837     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
838   }
839   return true;
840 }
841
842 //=======================================================================
843 //function : findTriangles
844 //purpose  : find triangles sharing theNode1-theNode2 link
845 //=======================================================================
846
847 static bool findTriangles(const SMDS_MeshNode *    theNode1,
848                           const SMDS_MeshNode *    theNode2,
849                           const SMDS_MeshElement*& theTria1,
850                           const SMDS_MeshElement*& theTria2)
851 {
852   if ( !theNode1 || !theNode2 ) return false;
853
854   theTria1 = theTria2 = 0;
855
856   set< const SMDS_MeshElement* > emap;
857   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
858   while (it->more()) {
859     const SMDS_MeshElement* elem = it->next();
860     if ( elem->NbCornerNodes() == 3 )
861       emap.insert( elem );
862   }
863   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
864   while (it->more()) {
865     const SMDS_MeshElement* elem = it->next();
866     if ( emap.count( elem )) {
867       if ( !theTria1 )
868       {
869         theTria1 = elem;
870       }
871       else  
872       {
873         theTria2 = elem;
874         // theTria1 must be element with minimum ID
875         if ( theTria2->GetID() < theTria1->GetID() )
876           std::swap( theTria2, theTria1 );
877         return true;
878       }
879     }
880   }
881   return false;
882 }
883
884 //=======================================================================
885 //function : InverseDiag
886 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
887 //           with ones built on the same 4 nodes but having other common link.
888 //           Return false if proper faces not found
889 //=======================================================================
890
891 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
892                                     const SMDS_MeshNode * theNode2)
893 {
894   myLastCreatedElems.Clear();
895   myLastCreatedNodes.Clear();
896
897   const SMDS_MeshElement *tr1, *tr2;
898   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
899     return false;
900
901   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
902   if (!F1) return false;
903   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
904   if (!F2) return false;
905   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
906       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
907
908     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
909     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
910     //    |/ |                                    | \|
911     //  B +--+ 2                                B +--+ 2
912
913     // put nodes in array
914     // and find indices of 1,2 and of A in tr1 and of B in tr2
915     int i, iA1 = 0, i1 = 0;
916     const SMDS_MeshNode* aNodes1 [3];
917     SMDS_ElemIteratorPtr it;
918     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
919       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
920       if ( aNodes1[ i ] == theNode1 )
921         iA1 = i; // node A in tr1
922       else if ( aNodes1[ i ] != theNode2 )
923         i1 = i;  // node 1
924     }
925     int iB2 = 0, i2 = 0;
926     const SMDS_MeshNode* aNodes2 [3];
927     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
928       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
929       if ( aNodes2[ i ] == theNode2 )
930         iB2 = i; // node B in tr2
931       else if ( aNodes2[ i ] != theNode1 )
932         i2 = i;  // node 2
933     }
934
935     // nodes 1 and 2 should not be the same
936     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
937       return false;
938
939     // tr1: A->2
940     aNodes1[ iA1 ] = aNodes2[ i2 ];
941     // tr2: B->1
942     aNodes2[ iB2 ] = aNodes1[ i1 ];
943
944     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
945     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
946
947     return true;
948   }
949
950   // check case of quadratic faces
951   return InverseDiag(tr1,tr2);
952 }
953
954 //=======================================================================
955 //function : getQuadrangleNodes
956 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
957 //           fusion of triangles tr1 and tr2 having shared link on
958 //           theNode1 and theNode2
959 //=======================================================================
960
961 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
962                         const SMDS_MeshNode *    theNode1,
963                         const SMDS_MeshNode *    theNode2,
964                         const SMDS_MeshElement * tr1,
965                         const SMDS_MeshElement * tr2 )
966 {
967   if( tr1->NbNodes() != tr2->NbNodes() )
968     return false;
969   // find the 4-th node to insert into tr1
970   const SMDS_MeshNode* n4 = 0;
971   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
972   int i=0;
973   while ( !n4 && i<3 ) {
974     const SMDS_MeshNode * n = cast2Node( it->next() );
975     i++;
976     bool isDiag = ( n == theNode1 || n == theNode2 );
977     if ( !isDiag )
978       n4 = n;
979   }
980   // Make an array of nodes to be in a quadrangle
981   int iNode = 0, iFirstDiag = -1;
982   it = tr1->nodesIterator();
983   i=0;
984   while ( i<3 ) {
985     const SMDS_MeshNode * n = cast2Node( it->next() );
986     i++;
987     bool isDiag = ( n == theNode1 || n == theNode2 );
988     if ( isDiag ) {
989       if ( iFirstDiag < 0 )
990         iFirstDiag = iNode;
991       else if ( iNode - iFirstDiag == 1 )
992         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
993     }
994     else if ( n == n4 ) {
995       return false; // tr1 and tr2 should not have all the same nodes
996     }
997     theQuadNodes[ iNode++ ] = n;
998   }
999   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1000     theQuadNodes[ iNode ] = n4;
1001
1002   return true;
1003 }
1004
1005 //=======================================================================
1006 //function : DeleteDiag
1007 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1008 //           with a quadrangle built on the same 4 nodes.
1009 //           Return false if proper faces not found
1010 //=======================================================================
1011
1012 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1013                                    const SMDS_MeshNode * theNode2)
1014 {
1015   myLastCreatedElems.Clear();
1016   myLastCreatedNodes.Clear();
1017
1018   const SMDS_MeshElement *tr1, *tr2;
1019   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1020     return false;
1021
1022   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1023   if (!F1) return false;
1024   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1025   if (!F2) return false;
1026   SMESHDS_Mesh * aMesh = GetMeshDS();
1027
1028   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1029       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1030
1031     const SMDS_MeshNode* aNodes [ 4 ];
1032     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1033       return false;
1034
1035     const SMDS_MeshElement* newElem = 0;
1036     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1037     myLastCreatedElems.Append(newElem);
1038     AddToSameGroups( newElem, tr1, aMesh );
1039     int aShapeId = tr1->getshapeId();
1040     if ( aShapeId )
1041       {
1042         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1043       }
1044     aMesh->RemoveElement( tr1 );
1045     aMesh->RemoveElement( tr2 );
1046
1047     return true;
1048   }
1049
1050   // check case of quadratic faces
1051   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1052     return false;
1053   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1054     return false;
1055
1056   //       5
1057   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1058   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1059   //    |   / |
1060   //  7 +  +  + 6
1061   //    | /9  |
1062   //    |/    |
1063   //  4 +--+--+ 3
1064   //       8
1065
1066   vector< const SMDS_MeshNode* > N1;
1067   vector< const SMDS_MeshNode* > N2;
1068   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1069     return false;
1070   // now we receive following N1 and N2 (using numeration as above image)
1071   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1072   // i.e. first nodes from both arrays determ new diagonal
1073
1074   const SMDS_MeshNode* aNodes[8];
1075   aNodes[0] = N1[0];
1076   aNodes[1] = N1[1];
1077   aNodes[2] = N2[0];
1078   aNodes[3] = N2[1];
1079   aNodes[4] = N1[3];
1080   aNodes[5] = N2[5];
1081   aNodes[6] = N2[3];
1082   aNodes[7] = N1[5];
1083
1084   const SMDS_MeshElement* newElem = 0;
1085   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1086                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1087   myLastCreatedElems.Append(newElem);
1088   AddToSameGroups( newElem, tr1, aMesh );
1089   int aShapeId = tr1->getshapeId();
1090   if ( aShapeId )
1091     {
1092       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1093     }
1094   aMesh->RemoveElement( tr1 );
1095   aMesh->RemoveElement( tr2 );
1096
1097   // remove middle node (9)
1098   GetMeshDS()->RemoveNode( N1[4] );
1099
1100   return true;
1101 }
1102
1103 //=======================================================================
1104 //function : Reorient
1105 //purpose  : Reverse theElement orientation
1106 //=======================================================================
1107
1108 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1109 {
1110   myLastCreatedElems.Clear();
1111   myLastCreatedNodes.Clear();
1112
1113   if (!theElem)
1114     return false;
1115   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1116   if ( !it || !it->more() )
1117     return false;
1118
1119   const SMDSAbs_ElementType type = theElem->GetType();
1120   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1121     return false;
1122
1123   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1124   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1125   {
1126     const SMDS_VtkVolume* aPolyedre =
1127       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1128     if (!aPolyedre) {
1129       MESSAGE("Warning: bad volumic element");
1130       return false;
1131     }
1132     const int nbFaces = aPolyedre->NbFaces();
1133     vector<const SMDS_MeshNode *> poly_nodes;
1134     vector<int> quantities (nbFaces);
1135
1136     // reverse each face of the polyedre
1137     for (int iface = 1; iface <= nbFaces; iface++) {
1138       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1139       quantities[iface - 1] = nbFaceNodes;
1140
1141       for (inode = nbFaceNodes; inode >= 1; inode--) {
1142         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1143         poly_nodes.push_back(curNode);
1144       }
1145     }
1146     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1147   }
1148   else // other elements
1149   {
1150     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1151     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1152     if ( interlace.empty() )
1153     {
1154       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1155     }
1156     else
1157     {
1158       SMDS_MeshCell::applyInterlace( interlace, nodes );
1159     }
1160     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1161   }
1162   return false;
1163 }
1164
1165 //================================================================================
1166 /*!
1167  * \brief Reorient faces.
1168  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1169  * \param theDirection - desired direction of normal of \a theFace
1170  * \param theFace - one of \a theFaces that should be oriented according to
1171  *        \a theDirection and whose orientation defines orientation of other faces
1172  * \return number of reoriented faces.
1173  */
1174 //================================================================================
1175
1176 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1177                                   const gp_Dir&            theDirection,
1178                                   const SMDS_MeshElement * theFace)
1179 {
1180   int nbReori = 0;
1181   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1182
1183   if ( theFaces.empty() )
1184   {
1185     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1186     while ( fIt->more() )
1187       theFaces.insert( theFaces.end(), fIt->next() );
1188   }
1189
1190   // orient theFace according to theDirection
1191   gp_XYZ normal;
1192   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1193   if ( normal * theDirection.XYZ() < 0 )
1194     nbReori += Reorient( theFace );
1195
1196   // Orient other faces
1197
1198   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1199   TIDSortedElemSet avoidSet;
1200   set< SMESH_TLink > checkedLinks;
1201   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1202
1203   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1204     theFaces.erase( theFace );
1205   startFaces.insert( theFace );
1206
1207   int nodeInd1, nodeInd2;
1208   const SMDS_MeshElement*           otherFace;
1209   vector< const SMDS_MeshElement* > facesNearLink;
1210   vector< std::pair< int, int > >   nodeIndsOfFace;
1211
1212   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1213   while ( !startFaces.empty() )
1214   {
1215     startFace = startFaces.begin();
1216     theFace = *startFace;
1217     startFaces.erase( startFace );
1218     if ( !visitedFaces.insert( theFace ).second )
1219       continue;
1220
1221     avoidSet.clear();
1222     avoidSet.insert(theFace);
1223
1224     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1225
1226     const int nbNodes = theFace->NbCornerNodes();
1227     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1228     {
1229       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1230       linkIt_isNew = checkedLinks.insert( link );
1231       if ( !linkIt_isNew.second )
1232       {
1233         // link has already been checked and won't be encountered more
1234         // if the group (theFaces) is manifold
1235         //checkedLinks.erase( linkIt_isNew.first );
1236       }
1237       else
1238       {
1239         facesNearLink.clear();
1240         nodeIndsOfFace.clear();
1241         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1242                                                              theFaces, avoidSet,
1243                                                              &nodeInd1, &nodeInd2 )))
1244           if ( otherFace != theFace)
1245           {
1246             facesNearLink.push_back( otherFace );
1247             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1248             avoidSet.insert( otherFace );
1249           }
1250         if ( facesNearLink.size() > 1 )
1251         {
1252           // NON-MANIFOLD mesh shell !
1253           // select a face most co-directed with theFace,
1254           // other faces won't be visited this time
1255           gp_XYZ NF, NOF;
1256           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1257           double proj, maxProj = -1;
1258           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1259             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1260             if (( proj = Abs( NF * NOF )) > maxProj ) {
1261               maxProj = proj;
1262               otherFace = facesNearLink[i];
1263               nodeInd1  = nodeIndsOfFace[i].first;
1264               nodeInd2  = nodeIndsOfFace[i].second;
1265             }
1266           }
1267           // not to visit rejected faces
1268           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1269             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1270               visitedFaces.insert( facesNearLink[i] );
1271         }
1272         else if ( facesNearLink.size() == 1 )
1273         {
1274           otherFace = facesNearLink[0];
1275           nodeInd1  = nodeIndsOfFace.back().first;
1276           nodeInd2  = nodeIndsOfFace.back().second;
1277         }
1278         if ( otherFace && otherFace != theFace)
1279         {
1280           // link must be reverse in otherFace if orientation ot otherFace
1281           // is same as that of theFace
1282           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1283           {
1284             nbReori += Reorient( otherFace );
1285           }
1286           startFaces.insert( otherFace );
1287         }
1288       }
1289       std::swap( link.first, link.second ); // reverse the link
1290     }
1291   }
1292   return nbReori;
1293 }
1294
1295 //================================================================================
1296 /*!
1297  * \brief Reorient faces basing on orientation of adjacent volumes.
1298  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1299  * \param theVolumes - reference volumes.
1300  * \param theOutsideNormal - to orient faces to have their normal
1301  *        pointing either \a outside or \a inside the adjacent volumes.
1302  * \return number of reoriented faces.
1303  */
1304 //================================================================================
1305
1306 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1307                                       TIDSortedElemSet & theVolumes,
1308                                       const bool         theOutsideNormal)
1309 {
1310   int nbReori = 0;
1311
1312   SMDS_ElemIteratorPtr faceIt;
1313   if ( theFaces.empty() )
1314     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1315   else
1316     faceIt = elemSetIterator( theFaces );
1317
1318   vector< const SMDS_MeshNode* > faceNodes;
1319   TIDSortedElemSet checkedVolumes;
1320   set< const SMDS_MeshNode* > faceNodesSet;
1321   SMDS_VolumeTool volumeTool;
1322
1323   while ( faceIt->more() ) // loop on given faces
1324   {
1325     const SMDS_MeshElement* face = faceIt->next();
1326     if ( face->GetType() != SMDSAbs_Face )
1327       continue;
1328
1329     const size_t nbCornersNodes = face->NbCornerNodes();
1330     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1331
1332     checkedVolumes.clear();
1333     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1334     while ( vIt->more() )
1335     {
1336       const SMDS_MeshElement* volume = vIt->next();
1337
1338       if ( !checkedVolumes.insert( volume ).second )
1339         continue;
1340       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1341         continue;
1342
1343       // is volume adjacent?
1344       bool allNodesCommon = true;
1345       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1346         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1347       if ( !allNodesCommon )
1348         continue;
1349
1350       // get nodes of a corresponding volume facet
1351       faceNodesSet.clear();
1352       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1353       volumeTool.Set( volume );
1354       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1355       if ( facetID < 0 ) continue;
1356       volumeTool.SetExternalNormal();
1357       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1358
1359       // compare order of faceNodes and facetNodes
1360       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1361       int iNN[2];
1362       for ( int i = 0; i < 2; ++i )
1363       {
1364         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1365         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1366           if ( faceNodes[ iN ] == n )
1367           {
1368             iNN[ i ] = iN;
1369             break;
1370           }
1371       }
1372       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1373       if ( isOutside != theOutsideNormal )
1374         nbReori += Reorient( face );
1375     }
1376   }  // loop on given faces
1377
1378   return nbReori;
1379 }
1380
1381 //=======================================================================
1382 //function : getBadRate
1383 //purpose  :
1384 //=======================================================================
1385
1386 static double getBadRate (const SMDS_MeshElement*               theElem,
1387                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1388 {
1389   SMESH::Controls::TSequenceOfXYZ P;
1390   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1391     return 1e100;
1392   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1393   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1394 }
1395
1396 //=======================================================================
1397 //function : QuadToTri
1398 //purpose  : Cut quadrangles into triangles.
1399 //           theCrit is used to select a diagonal to cut
1400 //=======================================================================
1401
1402 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1403                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1404 {
1405   myLastCreatedElems.Clear();
1406   myLastCreatedNodes.Clear();
1407
1408   if ( !theCrit.get() )
1409     return false;
1410
1411   SMESHDS_Mesh * aMesh = GetMeshDS();
1412
1413   Handle(Geom_Surface) surface;
1414   SMESH_MesherHelper   helper( *GetMesh() );
1415
1416   TIDSortedElemSet::iterator itElem;
1417   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1418   {
1419     const SMDS_MeshElement* elem = *itElem;
1420     if ( !elem || elem->GetType() != SMDSAbs_Face )
1421       continue;
1422     if ( elem->NbCornerNodes() != 4 )
1423       continue;
1424
1425     // retrieve element nodes
1426     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1427
1428     // compare two sets of possible triangles
1429     double aBadRate1, aBadRate2; // to what extent a set is bad
1430     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1431     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1432     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433
1434     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1435     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1436     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437
1438     const int aShapeId = FindShape( elem );
1439     const SMDS_MeshElement* newElem1 = 0;
1440     const SMDS_MeshElement* newElem2 = 0;
1441
1442     if ( !elem->IsQuadratic() ) // split liner quadrangle
1443     {
1444       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1445       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1446       if ( aBadRate1 <= aBadRate2 ) {
1447         // tr1 + tr2 is better
1448         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1449         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1450       }
1451       else {
1452         // tr3 + tr4 is better
1453         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1454         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1455       }
1456     }
1457     else // split quadratic quadrangle
1458     {
1459       helper.SetIsQuadratic( true );
1460       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1461
1462       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1463       if ( aNodes.size() == 9 )
1464       {
1465         helper.SetIsBiQuadratic( true );
1466         if ( aBadRate1 <= aBadRate2 )
1467           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1468         else
1469           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1470       }
1471       // create a new element
1472       if ( aBadRate1 <= aBadRate2 ) {
1473         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1474         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1475       }
1476       else {
1477         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1478         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1479       }
1480     } // quadratic case
1481
1482     // care of a new element
1483
1484     myLastCreatedElems.Append(newElem1);
1485     myLastCreatedElems.Append(newElem2);
1486     AddToSameGroups( newElem1, elem, aMesh );
1487     AddToSameGroups( newElem2, elem, aMesh );
1488
1489     // put a new triangle on the same shape
1490     if ( aShapeId )
1491       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1492     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1493
1494     aMesh->RemoveElement( elem );
1495   }
1496   return true;
1497 }
1498
1499 //=======================================================================
1500 /*!
1501  * \brief Split each of given quadrangles into 4 triangles.
1502  * \param theElems - The faces to be splitted. If empty all faces are split.
1503  */
1504 //=======================================================================
1505
1506 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1507 {
1508   myLastCreatedElems.Clear();
1509   myLastCreatedNodes.Clear();
1510
1511   SMESH_MesherHelper helper( *GetMesh() );
1512   helper.SetElementsOnShape( true );
1513
1514   SMDS_ElemIteratorPtr faceIt;
1515   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1516   else                    faceIt = elemSetIterator( theElems );
1517
1518   bool   checkUV;
1519   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1520   gp_XYZ xyz[9];
1521   vector< const SMDS_MeshNode* > nodes;
1522   SMESHDS_SubMesh*               subMeshDS = 0;
1523   TopoDS_Face                    F;
1524   Handle(Geom_Surface)           surface;
1525   TopLoc_Location                loc;
1526
1527   while ( faceIt->more() )
1528   {
1529     const SMDS_MeshElement* quad = faceIt->next();
1530     if ( !quad || quad->NbCornerNodes() != 4 )
1531       continue;
1532
1533     // get a surface the quad is on
1534
1535     if ( quad->getshapeId() < 1 )
1536     {
1537       F.Nullify();
1538       helper.SetSubShape( 0 );
1539       subMeshDS = 0;
1540     }
1541     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542     {
1543       helper.SetSubShape( quad->getshapeId() );
1544       if ( !helper.GetSubShape().IsNull() &&
1545            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546       {
1547         F = TopoDS::Face( helper.GetSubShape() );
1548         surface = BRep_Tool::Surface( F, loc );
1549         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1550       }
1551       else
1552       {
1553         helper.SetSubShape( 0 );
1554         subMeshDS = 0;
1555       }
1556     }
1557
1558     // create a central node
1559
1560     const SMDS_MeshNode* nCentral;
1561     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562
1563     if ( nodes.size() == 9 )
1564     {
1565       nCentral = nodes.back();
1566     }
1567     else
1568     {
1569       size_t iN = 0;
1570       if ( F.IsNull() )
1571       {
1572         for ( ; iN < nodes.size(); ++iN )
1573           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1574
1575         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1576           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577
1578         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1579                                    xyz[0], xyz[1], xyz[2], xyz[3],
1580                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1581       }
1582       else
1583       {
1584         for ( ; iN < nodes.size(); ++iN )
1585           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586
1587         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1588           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589
1590         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1591                                   uv[0], uv[1], uv[2], uv[3],
1592                                   uv[4], uv[5], uv[6], uv[7] );
1593
1594         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1595         xyz[ 8 ] = p.XYZ();
1596       }
1597
1598       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1599                                  uv[8].X(), uv[8].Y() );
1600       myLastCreatedNodes.Append( nCentral );
1601     }
1602
1603     // create 4 triangles
1604
1605     helper.SetIsQuadratic  ( nodes.size() > 4 );
1606     helper.SetIsBiQuadratic( nodes.size() == 9 );
1607     if ( helper.GetIsQuadratic() )
1608       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1609
1610     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1611
1612     for ( int i = 0; i < 4; ++i )
1613     {
1614       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1615                                                nodes[(i+1)%4],
1616                                                nCentral );
1617       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1618       myLastCreatedElems.Append( tria );
1619     }
1620   }
1621 }
1622
1623 //=======================================================================
1624 //function : BestSplit
1625 //purpose  : Find better diagonal for cutting.
1626 //=======================================================================
1627
1628 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1629                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1630 {
1631   myLastCreatedElems.Clear();
1632   myLastCreatedNodes.Clear();
1633
1634   if (!theCrit.get())
1635     return -1;
1636
1637   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1638     return -1;
1639
1640   if( theQuad->NbNodes()==4 ||
1641       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1642
1643     // retrieve element nodes
1644     const SMDS_MeshNode* aNodes [4];
1645     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1646     int i = 0;
1647     //while (itN->more())
1648     while (i<4) {
1649       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1650     }
1651     // compare two sets of possible triangles
1652     double aBadRate1, aBadRate2; // to what extent a set is bad
1653     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1654     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1655     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1656
1657     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1658     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1659     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1660     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1661     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1662     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1663       return 1; // diagonal 1-3
1664
1665     return 2; // diagonal 2-4
1666   }
1667   return -1;
1668 }
1669
1670 namespace
1671 {
1672   // Methods of splitting volumes into tetra
1673
1674   const int theHexTo5_1[5*4+1] =
1675     {
1676       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1677     };
1678   const int theHexTo5_2[5*4+1] =
1679     {
1680       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1681     };
1682   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1683
1684   const int theHexTo6_1[6*4+1] =
1685     {
1686       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
1687     };
1688   const int theHexTo6_2[6*4+1] =
1689     {
1690       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
1691     };
1692   const int theHexTo6_3[6*4+1] =
1693     {
1694       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
1695     };
1696   const int theHexTo6_4[6*4+1] =
1697     {
1698       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
1699     };
1700   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1701
1702   const int thePyraTo2_1[2*4+1] =
1703     {
1704       0, 1, 2, 4,    0, 2, 3, 4,   -1
1705     };
1706   const int thePyraTo2_2[2*4+1] =
1707     {
1708       1, 2, 3, 4,    1, 3, 0, 4,   -1
1709     };
1710   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1711
1712   const int thePentaTo3_1[3*4+1] =
1713     {
1714       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1715     };
1716   const int thePentaTo3_2[3*4+1] =
1717     {
1718       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1719     };
1720   const int thePentaTo3_3[3*4+1] =
1721     {
1722       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1723     };
1724   const int thePentaTo3_4[3*4+1] =
1725     {
1726       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1727     };
1728   const int thePentaTo3_5[3*4+1] =
1729     {
1730       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1731     };
1732   const int thePentaTo3_6[3*4+1] =
1733     {
1734       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1735     };
1736   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1737                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1738
1739   // Methods of splitting hexahedron into prisms
1740
1741   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1742     {
1743       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
1744     };
1745   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1746     {
1747       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
1748     };
1749   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1750     {
1751       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
1752     };
1753
1754   const int theHexTo2Prisms_BT_1[6*2+1] =
1755     {
1756       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1757     };
1758   const int theHexTo2Prisms_BT_2[6*2+1] =
1759     {
1760       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1761     };
1762   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1763
1764   const int theHexTo2Prisms_LR_1[6*2+1] =
1765     {
1766       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1767     };
1768   const int theHexTo2Prisms_LR_2[6*2+1] =
1769     {
1770       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1771     };
1772   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1773
1774   const int theHexTo2Prisms_FB_1[6*2+1] =
1775     {
1776       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1777     };
1778   const int theHexTo2Prisms_FB_2[6*2+1] =
1779     {
1780       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1781     };
1782   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1783
1784
1785   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1786   {
1787     int _n1, _n2, _n3;
1788     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1789     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1790     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1791                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1792   };
1793   struct TSplitMethod
1794   {
1795     int        _nbSplits;
1796     int        _nbCorners;
1797     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1798     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1799     bool       _ownConn;      //!< to delete _connectivity in destructor
1800     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1801
1802     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1803       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1804     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1805     bool hasFacet( const TTriangleFacet& facet ) const
1806     {
1807       if ( _nbCorners == 4 )
1808       {
1809         const int* tetConn = _connectivity;
1810         for ( ; tetConn[0] >= 0; tetConn += 4 )
1811           if (( facet.contains( tetConn[0] ) +
1812                 facet.contains( tetConn[1] ) +
1813                 facet.contains( tetConn[2] ) +
1814                 facet.contains( tetConn[3] )) == 3 )
1815             return true;
1816       }
1817       else // prism, _nbCorners == 6
1818       {
1819         const int* prismConn = _connectivity;
1820         for ( ; prismConn[0] >= 0; prismConn += 6 )
1821         {
1822           if (( facet.contains( prismConn[0] ) &&
1823                 facet.contains( prismConn[1] ) &&
1824                 facet.contains( prismConn[2] ))
1825               ||
1826               ( facet.contains( prismConn[3] ) &&
1827                 facet.contains( prismConn[4] ) &&
1828                 facet.contains( prismConn[5] )))
1829             return true;
1830         }
1831       }
1832       return false;
1833     }
1834   };
1835
1836   //=======================================================================
1837   /*!
1838    * \brief return TSplitMethod for the given element to split into tetrahedra
1839    */
1840   //=======================================================================
1841
1842   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1843   {
1844     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1845
1846     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1847     // an edge and a face barycenter; tertaherdons are based on triangles and
1848     // a volume barycenter
1849     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1850
1851     // Find out how adjacent volumes are split
1852
1853     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1854     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1855     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1856     {
1857       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1858       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1859       if ( nbNodes < 4 ) continue;
1860
1861       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1862       const int* nInd = vol.GetFaceNodesIndices( iF );
1863       if ( nbNodes == 4 )
1864       {
1865         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1866         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1867         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1868         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1869       }
1870       else
1871       {
1872         int iCom = 0; // common node of triangle faces to split into
1873         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1874         {
1875           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1876                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1877                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1878           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1879                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1880                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1881           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1882           {
1883             triaSplits.push_back( t012 );
1884             triaSplits.push_back( t023 );
1885             break;
1886           }
1887         }
1888       }
1889       if ( !triaSplits.empty() )
1890         hasAdjacentSplits = true;
1891     }
1892
1893     // Among variants of split method select one compliant with adjacent volumes
1894
1895     TSplitMethod method;
1896     if ( !vol.Element()->IsPoly() && !is24TetMode )
1897     {
1898       int nbVariants = 2, nbTet = 0;
1899       const int** connVariants = 0;
1900       switch ( vol.Element()->GetEntityType() )
1901       {
1902       case SMDSEntity_Hexa:
1903       case SMDSEntity_Quad_Hexa:
1904       case SMDSEntity_TriQuad_Hexa:
1905         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1906           connVariants = theHexTo5, nbTet = 5;
1907         else
1908           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1909         break;
1910       case SMDSEntity_Pyramid:
1911       case SMDSEntity_Quad_Pyramid:
1912         connVariants = thePyraTo2;  nbTet = 2;
1913         break;
1914       case SMDSEntity_Penta:
1915       case SMDSEntity_Quad_Penta:
1916       case SMDSEntity_BiQuad_Penta:
1917         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1918         break;
1919       default:
1920         nbVariants = 0;
1921       }
1922       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1923       {
1924         // check method compliancy with adjacent tetras,
1925         // all found splits must be among facets of tetras described by this method
1926         method = TSplitMethod( nbTet, connVariants[variant] );
1927         if ( hasAdjacentSplits && method._nbSplits > 0 )
1928         {
1929           bool facetCreated = true;
1930           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1931           {
1932             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1933             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1934               facetCreated = method.hasFacet( *facet );
1935           }
1936           if ( !facetCreated )
1937             method = TSplitMethod(0); // incompatible method
1938         }
1939       }
1940     }
1941     if ( method._nbSplits < 1 )
1942     {
1943       // No standard method is applicable, use a generic solution:
1944       // each facet of a volume is split into triangles and
1945       // each of triangles and a volume barycenter form a tetrahedron.
1946
1947       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1948
1949       int* connectivity = new int[ maxTetConnSize + 1 ];
1950       method._connectivity = connectivity;
1951       method._ownConn = true;
1952       method._baryNode = !isHex27; // to create central node or not
1953
1954       int connSize = 0;
1955       int baryCenInd = vol.NbNodes() - int( isHex27 );
1956       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1957       {
1958         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1959         const int*   nInd = vol.GetFaceNodesIndices( iF );
1960         // find common node of triangle facets of tetra to create
1961         int iCommon = 0; // index in linear numeration
1962         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1963         if ( !triaSplits.empty() )
1964         {
1965           // by found facets
1966           const TTriangleFacet* facet = &triaSplits.front();
1967           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1968             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1969                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970               break;
1971         }
1972         else if ( nbNodes > 3 && !is24TetMode )
1973         {
1974           // find the best method of splitting into triangles by aspect ratio
1975           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1976           map< double, int > badness2iCommon;
1977           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1978           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1979           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980           {
1981             double badness = 0;
1982             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1983             {
1984               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1985                                       nodes[ iQ*((iLast-1)%nbNodes)],
1986                                       nodes[ iQ*((iLast  )%nbNodes)]);
1987               badness += getBadRate( &tria, aspectRatio );
1988             }
1989             badness2iCommon.insert( make_pair( badness, iCommon ));
1990           }
1991           // use iCommon with lowest badness
1992           iCommon = badness2iCommon.begin()->second;
1993         }
1994         if ( iCommon >= nbNodes )
1995           iCommon = 0; // something wrong
1996
1997         // fill connectivity of tetrahedra based on a current face
1998         int nbTet = nbNodes - 2;
1999         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2000         {
2001           int faceBaryCenInd;
2002           if ( isHex27 )
2003           {
2004             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2005             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2006           }
2007           else
2008           {
2009             method._faceBaryNode[ iF ] = 0;
2010             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011           }
2012           nbTet = nbNodes;
2013           for ( int i = 0; i < nbTet; ++i )
2014           {
2015             int i1 = i, i2 = (i+1) % nbNodes;
2016             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2017             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2018             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2019             connectivity[ connSize++ ] = faceBaryCenInd;
2020             connectivity[ connSize++ ] = baryCenInd;
2021           }
2022         }
2023         else
2024         {
2025           for ( int i = 0; i < nbTet; ++i )
2026           {
2027             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2028             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2029             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2030             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2031             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2032             connectivity[ connSize++ ] = baryCenInd;
2033           }
2034         }
2035         method._nbSplits += nbTet;
2036
2037       } // loop on volume faces
2038
2039       connectivity[ connSize++ ] = -1;
2040
2041     } // end of generic solution
2042
2043     return method;
2044   }
2045   //=======================================================================
2046   /*!
2047    * \brief return TSplitMethod to split haxhedron into prisms
2048    */
2049   //=======================================================================
2050
2051   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2052                                     const int        methodFlags,
2053                                     const int        facetToSplit)
2054   {
2055     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2056     // B, T, L, B, R, F
2057     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2058
2059     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2060     {
2061       static TSplitMethod to4methods[4]; // order BT, LR, FB
2062       if ( to4methods[iF]._nbSplits == 0 )
2063       {
2064         switch ( iF ) {
2065         case 0:
2066           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2067           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2068           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069           break;
2070         case 1:
2071           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2072           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2073           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074           break;
2075         case 2:
2076           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2077           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2078           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2079           break;
2080         default: return to4methods[3];
2081         }
2082         to4methods[iF]._nbSplits  = 4;
2083         to4methods[iF]._nbCorners = 6;
2084       }
2085       return to4methods[iF];
2086     }
2087     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2088
2089     TSplitMethod method;
2090
2091     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2092
2093     const int nbVariants = 2, nbSplits = 2;
2094     const int** connVariants = 0;
2095     switch ( iF ) {
2096     case 0: connVariants = theHexTo2Prisms_BT; break;
2097     case 1: connVariants = theHexTo2Prisms_LR; break;
2098     case 2: connVariants = theHexTo2Prisms_FB; break;
2099     default: return method;
2100     }
2101
2102     // look for prisms adjacent via facetToSplit and an opposite one
2103     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2104     {
2105       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2106       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2107       if ( nbNodes != 4 ) return method;
2108
2109       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2110       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2111       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2112       TTriangleFacet* t;
2113       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2114         t = &t012;
2115       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2116         t = &t123;
2117       else
2118         continue;
2119
2120       // there are adjacent prism
2121       for ( int variant = 0; variant < nbVariants; ++variant )
2122       {
2123         // check method compliancy with adjacent prisms,
2124         // the found prism facets must be among facets of prisms described by current method
2125         method._nbSplits     = nbSplits;
2126         method._nbCorners    = 6;
2127         method._connectivity = connVariants[ variant ];
2128         if ( method.hasFacet( *t ))
2129           return method;
2130       }
2131     }
2132
2133     // No adjacent prisms. Select a variant with a best aspect ratio.
2134
2135     double badness[2] = { 0., 0. };
2136     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2137     const SMDS_MeshNode** nodes = vol.GetNodes();
2138     for ( int variant = 0; variant < nbVariants; ++variant )
2139       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2140       {
2141         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2142         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2143
2144         method._connectivity = connVariants[ variant ];
2145         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2146         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2147         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2148
2149         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150                                 nodes[ t->_n2 ],
2151                                 nodes[ t->_n3 ] );
2152         badness[ variant ] += getBadRate( &tria, aspectRatio );
2153       }
2154     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2155
2156     method._nbSplits     = nbSplits;
2157     method._nbCorners    = 6;
2158     method._connectivity = connVariants[ iBetter ];
2159
2160     return method;
2161   }
2162
2163   //================================================================================
2164   /*!
2165    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2166    */
2167   //================================================================================
2168
2169   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2170                                        const SMDSAbs_GeometryType geom ) const
2171   {
2172     // find the tetrahedron including the three nodes of facet
2173     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2174     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2175     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2176     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2177     while ( volIt1->more() )
2178     {
2179       const SMDS_MeshElement* v = volIt1->next();
2180       if ( v->GetGeomType() != geom )
2181         continue;
2182       const int lastCornerInd = v->NbCornerNodes() - 1;
2183       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2184         continue; // medium node not allowed
2185       const int ind2 = v->GetNodeIndex( n2 );
2186       if ( ind2 < 0 || lastCornerInd < ind2 )
2187         continue;
2188       const int ind3 = v->GetNodeIndex( n3 );
2189       if ( ind3 < 0 || lastCornerInd < ind3 )
2190         continue;
2191       return true;
2192     }
2193     return false;
2194   }
2195
2196   //=======================================================================
2197   /*!
2198    * \brief A key of a face of volume
2199    */
2200   //=======================================================================
2201
2202   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2203   {
2204     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2205     {
2206       TIDSortedNodeSet sortedNodes;
2207       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2208       int nbNodes = vol.NbFaceNodes( iF );
2209       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2210       for ( int i = 0; i < nbNodes; i += iQ )
2211         sortedNodes.insert( fNodes[i] );
2212       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2213       first.first   = (*(n++))->GetID();
2214       first.second  = (*(n++))->GetID();
2215       second.first  = (*(n++))->GetID();
2216       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2217     }
2218   };
2219 } // namespace
2220
2221 //=======================================================================
2222 //function : SplitVolumes
2223 //purpose  : Split volume elements into tetrahedra or prisms.
2224 //           If facet ID < 0, element is split into tetrahedra,
2225 //           else a hexahedron is split into prisms so that the given facet is
2226 //           split into triangles
2227 //=======================================================================
2228
2229 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2230                                      const int            theMethodFlags)
2231 {
2232   SMDS_VolumeTool    volTool;
2233   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2234   fHelper.ToFixNodeParameters( true );
2235
2236   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2237   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2238
2239   SMESH_SequenceOfElemPtr newNodes, newElems;
2240
2241   // map face of volume to it's baricenrtic node
2242   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2243   double bc[3];
2244   vector<const SMDS_MeshElement* > splitVols;
2245
2246   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2247   for ( ; elem2facet != theElems.end(); ++elem2facet )
2248   {
2249     const SMDS_MeshElement* elem = elem2facet->first;
2250     const int       facetToSplit = elem2facet->second;
2251     if ( elem->GetType() != SMDSAbs_Volume )
2252       continue;
2253     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2254     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2255       continue;
2256
2257     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2258
2259     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2260                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2261                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2262     if ( splitMethod._nbSplits < 1 ) continue;
2263
2264     // find submesh to add new tetras to
2265     if ( !subMesh || !subMesh->Contains( elem ))
2266     {
2267       int shapeID = FindShape( elem );
2268       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2269       subMesh = GetMeshDS()->MeshElements( shapeID );
2270     }
2271     int iQ;
2272     if ( elem->IsQuadratic() )
2273     {
2274       iQ = 2;
2275       // add quadratic links to the helper
2276       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2277       {
2278         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2279         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2280         for ( int iN = 0; iN < nbN; iN += iQ )
2281           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2282       }
2283       helper.SetIsQuadratic( true );
2284     }
2285     else
2286     {
2287       iQ = 1;
2288       helper.SetIsQuadratic( false );
2289     }
2290     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2291                                         volTool.GetNodes() + elem->NbNodes() );
2292     helper.SetElementsOnShape( true );
2293     if ( splitMethod._baryNode )
2294     {
2295       // make a node at barycenter
2296       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2297       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2298       nodes.push_back( gcNode );
2299       newNodes.Append( gcNode );
2300     }
2301     if ( !splitMethod._faceBaryNode.empty() )
2302     {
2303       // make or find baricentric nodes of faces
2304       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2305       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2306       {
2307         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2308           volFace2BaryNode.insert
2309           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2310         if ( !f_n->second )
2311         {
2312           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2313           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2314         }
2315         nodes.push_back( iF_n->second = f_n->second );
2316       }
2317     }
2318
2319     // make new volumes
2320     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2321     const int* volConn = splitMethod._connectivity;
2322     if ( splitMethod._nbCorners == 4 ) // tetra
2323       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2324         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2325                                                             nodes[ volConn[1] ],
2326                                                             nodes[ volConn[2] ],
2327                                                             nodes[ volConn[3] ]));
2328     else // prisms
2329       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2330         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2331                                                             nodes[ volConn[1] ],
2332                                                             nodes[ volConn[2] ],
2333                                                             nodes[ volConn[3] ],
2334                                                             nodes[ volConn[4] ],
2335                                                             nodes[ volConn[5] ]));
2336
2337     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2338
2339     // Split faces on sides of the split volume
2340
2341     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2342     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2343     {
2344       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2345       if ( nbNodes < 4 ) continue;
2346
2347       // find an existing face
2348       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2349                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2350       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2351                                                                        /*noMedium=*/false))
2352       {
2353         // make triangles
2354         helper.SetElementsOnShape( false );
2355         vector< const SMDS_MeshElement* > triangles;
2356
2357         // find submesh to add new triangles in
2358         if ( !fSubMesh || !fSubMesh->Contains( face ))
2359         {
2360           int shapeID = FindShape( face );
2361           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2362         }
2363         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2364         if ( iF_n != splitMethod._faceBaryNode.end() )
2365         {
2366           const SMDS_MeshNode *baryNode = iF_n->second;
2367           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2368           {
2369             const SMDS_MeshNode* n1 = fNodes[iN];
2370             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2371             const SMDS_MeshNode *n3 = baryNode;
2372             if ( !volTool.IsFaceExternal( iF ))
2373               swap( n2, n3 );
2374             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2375           }
2376           if ( fSubMesh ) // update position of the bary node on geometry
2377           {
2378             if ( subMesh )
2379               subMesh->RemoveNode( baryNode, false );
2380             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2381             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2382             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2383             {
2384               fHelper.SetSubShape( s );
2385               gp_XY uv( 1e100, 1e100 );
2386               double distXYZ[4];
2387               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2388                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2389                    uv.X() < 1e100 )
2390               {
2391                 // node is too far from the surface
2392                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2393                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2394                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2395               }
2396             }
2397           }
2398         }
2399         else
2400         {
2401           // among possible triangles create ones described by split method
2402           const int* nInd = volTool.GetFaceNodesIndices( iF );
2403           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2404           int iCom = 0; // common node of triangle faces to split into
2405           list< TTriangleFacet > facets;
2406           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2407           {
2408             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2409                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2410                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2411             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2412                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2413                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2414             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2415             {
2416               facets.push_back( t012 );
2417               facets.push_back( t023 );
2418               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2419                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2420                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2421                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2422               break;
2423             }
2424           }
2425           list< TTriangleFacet >::iterator facet = facets.begin();
2426           if ( facet == facets.end() )
2427             break;
2428           for ( ; facet != facets.end(); ++facet )
2429           {
2430             if ( !volTool.IsFaceExternal( iF ))
2431               swap( facet->_n2, facet->_n3 );
2432             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2433                                                  volNodes[ facet->_n2 ],
2434                                                  volNodes[ facet->_n3 ]));
2435           }
2436         }
2437         for ( size_t i = 0; i < triangles.size(); ++i )
2438         {
2439           if ( !triangles[ i ]) continue;
2440           if ( fSubMesh )
2441             fSubMesh->AddElement( triangles[ i ]);
2442           newElems.Append( triangles[ i ]);
2443         }
2444         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2445         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2446
2447       } // while a face based on facet nodes exists
2448     } // loop on volume faces to split them into triangles
2449
2450     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2451
2452     if ( geomType == SMDSEntity_TriQuad_Hexa )
2453     {
2454       // remove medium nodes that could become free
2455       for ( int i = 20; i < volTool.NbNodes(); ++i )
2456         if ( volNodes[i]->NbInverseElements() == 0 )
2457           GetMeshDS()->RemoveNode( volNodes[i] );
2458     }
2459   } // loop on volumes to split
2460
2461   myLastCreatedNodes = newNodes;
2462   myLastCreatedElems = newElems;
2463 }
2464
2465 //=======================================================================
2466 //function : GetHexaFacetsToSplit
2467 //purpose  : For hexahedra that will be split into prisms, finds facets to
2468 //           split into triangles. Only hexahedra adjacent to the one closest
2469 //           to theFacetNormal.Location() are returned.
2470 //param [in,out] theHexas - the hexahedra
2471 //param [in]     theFacetNormal - facet normal
2472 //param [out]    theFacets - the hexahedra and found facet IDs
2473 //=======================================================================
2474
2475 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2476                                              const gp_Ax1&     theFacetNormal,
2477                                              TFacetOfElem &    theFacets)
2478 {
2479   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2480
2481   // Find a hexa closest to the location of theFacetNormal
2482
2483   const SMDS_MeshElement* startHex;
2484   {
2485     // get SMDS_ElemIteratorPtr on theHexas
2486     typedef const SMDS_MeshElement*                                      TValue;
2487     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2488     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2489     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2490     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2491     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2492       ( new TElemSetIter( theHexas.begin(),
2493                           theHexas.end(),
2494                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2495
2496     SMESH_ElementSearcher* searcher =
2497       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2498
2499     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2500
2501     delete searcher;
2502
2503     if ( !startHex )
2504       throw SALOME_Exception( THIS_METHOD "startHex not found");
2505   }
2506
2507   // Select a facet of startHex by theFacetNormal
2508
2509   SMDS_VolumeTool vTool( startHex );
2510   double norm[3], dot, maxDot = 0;
2511   int facetID = -1;
2512   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2513     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2514     {
2515       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2516       if ( dot > maxDot )
2517       {
2518         facetID = iF;
2519         maxDot = dot;
2520       }
2521     }
2522   if ( facetID < 0 )
2523     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2524
2525   // Fill theFacets starting from facetID of startHex
2526
2527   // facets used for searching of volumes adjacent to already treated ones
2528   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2529   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2530   TFacetMap facetsToCheck;
2531
2532   set<const SMDS_MeshNode*> facetNodes;
2533   const SMDS_MeshElement*   curHex;
2534
2535   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2536
2537   while ( startHex )
2538   {
2539     // move in two directions from startHex via facetID
2540     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2541     {
2542       curHex       = startHex;
2543       int curFacet = facetID;
2544       if ( is2nd ) // do not treat startHex twice
2545       {
2546         vTool.Set( curHex );
2547         if ( vTool.IsFreeFace( curFacet, &curHex ))
2548         {
2549           curHex = 0;
2550         }
2551         else
2552         {
2553           vTool.GetFaceNodes( curFacet, facetNodes );
2554           vTool.Set( curHex );
2555           curFacet = vTool.GetFaceIndex( facetNodes );
2556         }
2557       }
2558       while ( curHex )
2559       {
2560         // store a facet to split
2561         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2562         {
2563           theFacets.insert( make_pair( curHex, -1 ));
2564           break;
2565         }
2566         if ( !allHex && !theHexas.count( curHex ))
2567           break;
2568
2569         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2570           theFacets.insert( make_pair( curHex, curFacet ));
2571         if ( !facetIt2isNew.second )
2572           break;
2573
2574         // remember not-to-split facets in facetsToCheck
2575         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2576         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2577         {
2578           if ( iF == curFacet && iF == oppFacet )
2579             continue;
2580           TVolumeFaceKey facetKey ( vTool, iF );
2581           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2582           pair< TFacetMap::iterator, bool > it2isnew =
2583             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2584           if ( !it2isnew.second )
2585             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2586         }
2587         // pass to a volume adjacent via oppFacet
2588         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2589         {
2590           curHex = 0;
2591         }
2592         else
2593         {
2594           // get a new curFacet
2595           vTool.GetFaceNodes( oppFacet, facetNodes );
2596           vTool.Set( curHex );
2597           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2598         }
2599       }
2600     } // move in two directions from startHex via facetID
2601
2602     // Find a new startHex by facetsToCheck
2603
2604     startHex = 0;
2605     facetID  = -1;
2606     TFacetMap::iterator fIt = facetsToCheck.begin();
2607     while ( !startHex && fIt != facetsToCheck.end() )
2608     {
2609       const TElemFacets&  elemFacets = fIt->second;
2610       const SMDS_MeshElement*    hex = elemFacets.first->first;
2611       int                 splitFacet = elemFacets.first->second;
2612       int               lateralFacet = elemFacets.second;
2613       facetsToCheck.erase( fIt );
2614       fIt = facetsToCheck.begin();
2615
2616       vTool.Set( hex );
2617       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2618            curHex->GetGeomType() != SMDSGeom_HEXA )
2619         continue;
2620       if ( !allHex && !theHexas.count( curHex ))
2621         continue;
2622
2623       startHex = curHex;
2624
2625       // find a facet of startHex to split
2626
2627       set<const SMDS_MeshNode*> lateralNodes;
2628       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2629       vTool.GetFaceNodes( splitFacet,   facetNodes );
2630       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2631       vTool.Set( startHex );
2632       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2633
2634       // look for a facet of startHex having common nodes with facetNodes
2635       // but not lateralFacet
2636       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2637       {
2638         if ( iF == lateralFacet )
2639           continue;
2640         int nbCommonNodes = 0;
2641         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2642         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2643           nbCommonNodes += facetNodes.count( nn[ iN ]);
2644
2645         if ( nbCommonNodes >= 2 )
2646         {
2647           facetID = iF;
2648           break;
2649         }
2650       }
2651       if ( facetID < 0 )
2652         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2653     }
2654   } //   while ( startHex )
2655
2656   return;
2657 }
2658
2659 namespace
2660 {
2661   //================================================================================
2662   /*!
2663    * \brief Selects nodes of several elements according to a given interlace
2664    *  \param [in] srcNodes - nodes to select from
2665    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2666    *  \param [in] interlace - indices of nodes for all elements
2667    *  \param [in] nbElems - nb of elements
2668    *  \param [in] nbNodes - nb of nodes in each element
2669    *  \param [in] mesh - the mesh
2670    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2671    *  \param [in] type - type of elements to look for
2672    */
2673   //================================================================================
2674
2675   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2676                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2677                     const int*                            interlace,
2678                     const int                             nbElems,
2679                     const int                             nbNodes,
2680                     SMESHDS_Mesh*                         mesh = 0,
2681                     list< const SMDS_MeshElement* >*      elemQueue=0,
2682                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2683   {
2684     for ( int iE = 0; iE < nbElems; ++iE )
2685     {
2686       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2687       const int*                         select = & interlace[iE*nbNodes];
2688       elemNodes.resize( nbNodes );
2689       for ( int iN = 0; iN < nbNodes; ++iN )
2690         elemNodes[iN] = srcNodes[ select[ iN ]];
2691     }
2692     const SMDS_MeshElement* e;
2693     if ( elemQueue )
2694       for ( int iE = 0; iE < nbElems; ++iE )
2695         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2696           elemQueue->push_back( e );
2697   }
2698 }
2699
2700 //=======================================================================
2701 /*
2702  * Split bi-quadratic elements into linear ones without creation of additional nodes
2703  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2704  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2705  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2706  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2707  *   will be split in order to keep the mesh conformal.
2708  *  \param elems - elements to split
2709  */
2710 //=======================================================================
2711
2712 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2713 {
2714   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2715   vector<const SMDS_MeshElement* > splitElems;
2716   list< const SMDS_MeshElement* > elemQueue;
2717   list< const SMDS_MeshElement* >::iterator elemIt;
2718
2719   SMESHDS_Mesh * mesh = GetMeshDS();
2720   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2721   int nbElems, nbNodes;
2722
2723   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2724   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2725   {
2726     elemQueue.clear();
2727     elemQueue.push_back( *elemSetIt );
2728     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2729     {
2730       const SMDS_MeshElement* elem = *elemIt;
2731       switch( elem->GetEntityType() )
2732       {
2733       case SMDSEntity_TriQuad_Hexa: // HEX27
2734       {
2735         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2736         nbElems  = nbNodes = 8;
2737         elemType = & hexaType;
2738
2739         // get nodes for new elements
2740         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2741                                  { 1,9,20,8,    17,22,26,21 },
2742                                  { 2,10,20,9,   18,23,26,22 },
2743                                  { 3,11,20,10,  19,24,26,23 },
2744                                  { 16,21,26,24, 4,12,25,15  },
2745                                  { 17,22,26,21, 5,13,25,12  },
2746                                  { 18,23,26,22, 6,14,25,13  },
2747                                  { 19,24,26,23, 7,15,25,14  }};
2748         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2749
2750         // add boundary faces to elemQueue
2751         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2752                                  { 4,5,6,7, 12,13,14,15, 25 },
2753                                  { 0,1,5,4, 8,17,12,16,  21 },
2754                                  { 1,2,6,5, 9,18,13,17,  22 },
2755                                  { 2,3,7,6, 10,19,14,18, 23 },
2756                                  { 3,0,4,7, 11,16,15,19, 24 }};
2757         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2758
2759         // add boundary segments to elemQueue
2760         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2761                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2762                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2763         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2764         break;
2765       }
2766       case SMDSEntity_BiQuad_Triangle: // TRIA7
2767       {
2768         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769         nbElems = 3;
2770         nbNodes = 4;
2771         elemType = & quadType;
2772
2773         // get nodes for new elements
2774         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2775         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2776
2777         // add boundary segments to elemQueue
2778         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2779         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2780         break;
2781       }
2782       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2783       {
2784         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785         nbElems = 4;
2786         nbNodes = 4;
2787         elemType = & quadType;
2788
2789         // get nodes for new elements
2790         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2791         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2792
2793         // add boundary segments to elemQueue
2794         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2795         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2796         break;
2797       }
2798       case SMDSEntity_Quad_Edge:
2799       {
2800         if ( elemIt == elemQueue.begin() )
2801           continue; // an elem is in theElems
2802         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2803         nbElems = 2;
2804         nbNodes = 2;
2805         elemType = & segType;
2806
2807         // get nodes for new elements
2808         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2809         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2810         break;
2811       }
2812       default: continue;
2813       } // switch( elem->GetEntityType() )
2814
2815       // Create new elements
2816
2817       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2818
2819       splitElems.clear();
2820
2821       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2822       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2823       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2824       //elemType->SetID( -1 );
2825
2826       for ( int iE = 0; iE < nbElems; ++iE )
2827         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2828
2829
2830       ReplaceElemInGroups( elem, splitElems, mesh );
2831
2832       if ( subMesh )
2833         for ( size_t i = 0; i < splitElems.size(); ++i )
2834           subMesh->AddElement( splitElems[i] );
2835     }
2836   }
2837 }
2838
2839 //=======================================================================
2840 //function : AddToSameGroups
2841 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2842 //=======================================================================
2843
2844 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2845                                         const SMDS_MeshElement* elemInGroups,
2846                                         SMESHDS_Mesh *          aMesh)
2847 {
2848   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2849   if (!groups.empty()) {
2850     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2851     for ( ; grIt != groups.end(); grIt++ ) {
2852       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2853       if ( group && group->Contains( elemInGroups ))
2854         group->SMDSGroup().Add( elemToAdd );
2855     }
2856   }
2857 }
2858
2859
2860 //=======================================================================
2861 //function : RemoveElemFromGroups
2862 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2863 //=======================================================================
2864 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2865                                              SMESHDS_Mesh *          aMesh)
2866 {
2867   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2868   if (!groups.empty())
2869   {
2870     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2871     for (; GrIt != groups.end(); GrIt++)
2872     {
2873       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2874       if (!grp || grp->IsEmpty()) continue;
2875       grp->SMDSGroup().Remove(removeelem);
2876     }
2877   }
2878 }
2879
2880 //================================================================================
2881 /*!
2882  * \brief Replace elemToRm by elemToAdd in the all groups
2883  */
2884 //================================================================================
2885
2886 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2887                                             const SMDS_MeshElement* elemToAdd,
2888                                             SMESHDS_Mesh *          aMesh)
2889 {
2890   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2891   if (!groups.empty()) {
2892     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2893     for ( ; grIt != groups.end(); grIt++ ) {
2894       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2895       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2896         group->SMDSGroup().Add( elemToAdd );
2897     }
2898   }
2899 }
2900
2901 //================================================================================
2902 /*!
2903  * \brief Replace elemToRm by elemToAdd in the all groups
2904  */
2905 //================================================================================
2906
2907 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2908                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2909                                             SMESHDS_Mesh *                         aMesh)
2910 {
2911   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2912   if (!groups.empty())
2913   {
2914     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2915     for ( ; grIt != groups.end(); grIt++ ) {
2916       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2917       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2918         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2919           group->SMDSGroup().Add( elemToAdd[ i ] );
2920     }
2921   }
2922 }
2923
2924 //=======================================================================
2925 //function : QuadToTri
2926 //purpose  : Cut quadrangles into triangles.
2927 //           theCrit is used to select a diagonal to cut
2928 //=======================================================================
2929
2930 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2931                                   const bool         the13Diag)
2932 {
2933   myLastCreatedElems.Clear();
2934   myLastCreatedNodes.Clear();
2935
2936   SMESHDS_Mesh * aMesh = GetMeshDS();
2937
2938   Handle(Geom_Surface) surface;
2939   SMESH_MesherHelper   helper( *GetMesh() );
2940
2941   TIDSortedElemSet::iterator itElem;
2942   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2943   {
2944     const SMDS_MeshElement* elem = *itElem;
2945     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2946       continue;
2947
2948     if ( elem->NbNodes() == 4 ) {
2949       // retrieve element nodes
2950       const SMDS_MeshNode* aNodes [4];
2951       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2952       int i = 0;
2953       while ( itN->more() )
2954         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2955
2956       int aShapeId = FindShape( elem );
2957       const SMDS_MeshElement* newElem1 = 0;
2958       const SMDS_MeshElement* newElem2 = 0;
2959       if ( the13Diag ) {
2960         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2961         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2962       }
2963       else {
2964         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2965         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2966       }
2967       myLastCreatedElems.Append(newElem1);
2968       myLastCreatedElems.Append(newElem2);
2969       // put a new triangle on the same shape and add to the same groups
2970       if ( aShapeId )
2971       {
2972         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2973         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2974       }
2975       AddToSameGroups( newElem1, elem, aMesh );
2976       AddToSameGroups( newElem2, elem, aMesh );
2977       aMesh->RemoveElement( elem );
2978     }
2979
2980     // Quadratic quadrangle
2981
2982     else if ( elem->NbNodes() >= 8 )
2983     {
2984       // get surface elem is on
2985       int aShapeId = FindShape( elem );
2986       if ( aShapeId != helper.GetSubShapeID() ) {
2987         surface.Nullify();
2988         TopoDS_Shape shape;
2989         if ( aShapeId > 0 )
2990           shape = aMesh->IndexToShape( aShapeId );
2991         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2992           TopoDS_Face face = TopoDS::Face( shape );
2993           surface = BRep_Tool::Surface( face );
2994           if ( !surface.IsNull() )
2995             helper.SetSubShape( shape );
2996         }
2997       }
2998
2999       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3000       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3001       for ( int i = 0; itN->more(); ++i )
3002         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3003
3004       const SMDS_MeshNode* centrNode = aNodes[8];
3005       if ( centrNode == 0 )
3006       {
3007         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3008                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3009                                            surface.IsNull() );
3010         myLastCreatedNodes.Append(centrNode);
3011       }
3012
3013       // create a new element
3014       const SMDS_MeshElement* newElem1 = 0;
3015       const SMDS_MeshElement* newElem2 = 0;
3016       if ( the13Diag ) {
3017         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3018                                   aNodes[6], aNodes[7], centrNode );
3019         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3020                                   centrNode, aNodes[4], aNodes[5] );
3021       }
3022       else {
3023         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3024                                   aNodes[7], aNodes[4], centrNode );
3025         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3026                                   centrNode, aNodes[5], aNodes[6] );
3027       }
3028       myLastCreatedElems.Append(newElem1);
3029       myLastCreatedElems.Append(newElem2);
3030       // put a new triangle on the same shape and add to the same groups
3031       if ( aShapeId )
3032       {
3033         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3034         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3035       }
3036       AddToSameGroups( newElem1, elem, aMesh );
3037       AddToSameGroups( newElem2, elem, aMesh );
3038       aMesh->RemoveElement( elem );
3039     }
3040   }
3041
3042   return true;
3043 }
3044
3045 //=======================================================================
3046 //function : getAngle
3047 //purpose  :
3048 //=======================================================================
3049
3050 double getAngle(const SMDS_MeshElement * tr1,
3051                 const SMDS_MeshElement * tr2,
3052                 const SMDS_MeshNode *    n1,
3053                 const SMDS_MeshNode *    n2)
3054 {
3055   double angle = 2. * M_PI; // bad angle
3056
3057   // get normals
3058   SMESH::Controls::TSequenceOfXYZ P1, P2;
3059   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3060        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3061     return angle;
3062   gp_Vec N1,N2;
3063   if(!tr1->IsQuadratic())
3064     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3065   else
3066     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3067   if ( N1.SquareMagnitude() <= gp::Resolution() )
3068     return angle;
3069   if(!tr2->IsQuadratic())
3070     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3071   else
3072     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3073   if ( N2.SquareMagnitude() <= gp::Resolution() )
3074     return angle;
3075
3076   // find the first diagonal node n1 in the triangles:
3077   // take in account a diagonal link orientation
3078   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3079   for ( int t = 0; t < 2; t++ ) {
3080     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3081     int i = 0, iDiag = -1;
3082     while ( it->more()) {
3083       const SMDS_MeshElement *n = it->next();
3084       if ( n == n1 || n == n2 ) {
3085         if ( iDiag < 0)
3086           iDiag = i;
3087         else {
3088           if ( i - iDiag == 1 )
3089             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3090           else
3091             nFirst[ t ] = n;
3092           break;
3093         }
3094       }
3095       i++;
3096     }
3097   }
3098   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3099     N2.Reverse();
3100
3101   angle = N1.Angle( N2 );
3102   //SCRUTE( angle );
3103   return angle;
3104 }
3105
3106 // =================================================
3107 // class generating a unique ID for a pair of nodes
3108 // and able to return nodes by that ID
3109 // =================================================
3110 class LinkID_Gen {
3111 public:
3112
3113   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3114     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3115   {}
3116
3117   long GetLinkID (const SMDS_MeshNode * n1,
3118                   const SMDS_MeshNode * n2) const
3119   {
3120     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3121   }
3122
3123   bool GetNodes (const long             theLinkID,
3124                  const SMDS_MeshNode* & theNode1,
3125                  const SMDS_MeshNode* & theNode2) const
3126   {
3127     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3128     if ( !theNode1 ) return false;
3129     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3130     if ( !theNode2 ) return false;
3131     return true;
3132   }
3133
3134 private:
3135   LinkID_Gen();
3136   const SMESHDS_Mesh* myMesh;
3137   long                myMaxID;
3138 };
3139
3140
3141 //=======================================================================
3142 //function : TriToQuad
3143 //purpose  : Fuse neighbour triangles into quadrangles.
3144 //           theCrit is used to select a neighbour to fuse with.
3145 //           theMaxAngle is a max angle between element normals at which
3146 //           fusion is still performed.
3147 //=======================================================================
3148
3149 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3150                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3151                                   const double                         theMaxAngle)
3152 {
3153   myLastCreatedElems.Clear();
3154   myLastCreatedNodes.Clear();
3155
3156   if ( !theCrit.get() )
3157     return false;
3158
3159   SMESHDS_Mesh * aMesh = GetMeshDS();
3160
3161   // Prepare data for algo: build
3162   // 1. map of elements with their linkIDs
3163   // 2. map of linkIDs with their elements
3164
3165   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3166   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3167   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3168   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3169
3170   TIDSortedElemSet::iterator itElem;
3171   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3172   {
3173     const SMDS_MeshElement* elem = *itElem;
3174     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3175     bool IsTria = ( elem->NbCornerNodes()==3 );
3176     if (!IsTria) continue;
3177
3178     // retrieve element nodes
3179     const SMDS_MeshNode* aNodes [4];
3180     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3181     int i = 0;
3182     while ( i < 3 )
3183       aNodes[ i++ ] = itN->next();
3184     aNodes[ 3 ] = aNodes[ 0 ];
3185
3186     // fill maps
3187     for ( i = 0; i < 3; i++ ) {
3188       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3189       // check if elements sharing a link can be fused
3190       itLE = mapLi_listEl.find( link );
3191       if ( itLE != mapLi_listEl.end() ) {
3192         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3193           continue;
3194         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3195         //if ( FindShape( elem ) != FindShape( elem2 ))
3196         //  continue; // do not fuse triangles laying on different shapes
3197         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3198           continue; // avoid making badly shaped quads
3199         (*itLE).second.push_back( elem );
3200       }
3201       else {
3202         mapLi_listEl[ link ].push_back( elem );
3203       }
3204       mapEl_setLi [ elem ].insert( link );
3205     }
3206   }
3207   // Clean the maps from the links shared by a sole element, ie
3208   // links to which only one element is bound in mapLi_listEl
3209
3210   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3211     int nbElems = (*itLE).second.size();
3212     if ( nbElems < 2  ) {
3213       const SMDS_MeshElement* elem = (*itLE).second.front();
3214       SMESH_TLink link = (*itLE).first;
3215       mapEl_setLi[ elem ].erase( link );
3216       if ( mapEl_setLi[ elem ].empty() )
3217         mapEl_setLi.erase( elem );
3218     }
3219   }
3220
3221   // Algo: fuse triangles into quadrangles
3222
3223   while ( ! mapEl_setLi.empty() ) {
3224     // Look for the start element:
3225     // the element having the least nb of shared links
3226     const SMDS_MeshElement* startElem = 0;
3227     int minNbLinks = 4;
3228     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3229       int nbLinks = (*itEL).second.size();
3230       if ( nbLinks < minNbLinks ) {
3231         startElem = (*itEL).first;
3232         minNbLinks = nbLinks;
3233         if ( minNbLinks == 1 )
3234           break;
3235       }
3236     }
3237
3238     // search elements to fuse starting from startElem or links of elements
3239     // fused earlyer - startLinks
3240     list< SMESH_TLink > startLinks;
3241     while ( startElem || !startLinks.empty() ) {
3242       while ( !startElem && !startLinks.empty() ) {
3243         // Get an element to start, by a link
3244         SMESH_TLink linkId = startLinks.front();
3245         startLinks.pop_front();
3246         itLE = mapLi_listEl.find( linkId );
3247         if ( itLE != mapLi_listEl.end() ) {
3248           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3249           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3250           for ( ; itE != listElem.end() ; itE++ )
3251             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3252               startElem = (*itE);
3253           mapLi_listEl.erase( itLE );
3254         }
3255       }
3256
3257       if ( startElem ) {
3258         // Get candidates to be fused
3259         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3260         const SMESH_TLink *link12 = 0, *link13 = 0;
3261         startElem = 0;
3262         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3263         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3264         ASSERT( !setLi.empty() );
3265         set< SMESH_TLink >::iterator itLi;
3266         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3267         {
3268           const SMESH_TLink & link = (*itLi);
3269           itLE = mapLi_listEl.find( link );
3270           if ( itLE == mapLi_listEl.end() )
3271             continue;
3272
3273           const SMDS_MeshElement* elem = (*itLE).second.front();
3274           if ( elem == tr1 )
3275             elem = (*itLE).second.back();
3276           mapLi_listEl.erase( itLE );
3277           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3278             continue;
3279           if ( tr2 ) {
3280             tr3 = elem;
3281             link13 = &link;
3282           }
3283           else {
3284             tr2 = elem;
3285             link12 = &link;
3286           }
3287
3288           // add other links of elem to list of links to re-start from
3289           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3290           set< SMESH_TLink >::iterator it;
3291           for ( it = links.begin(); it != links.end(); it++ ) {
3292             const SMESH_TLink& link2 = (*it);
3293             if ( link2 != link )
3294               startLinks.push_back( link2 );
3295           }
3296         }
3297
3298         // Get nodes of possible quadrangles
3299         const SMDS_MeshNode *n12 [4], *n13 [4];
3300         bool Ok12 = false, Ok13 = false;
3301         const SMDS_MeshNode *linkNode1, *linkNode2;
3302         if(tr2) {
3303           linkNode1 = link12->first;
3304           linkNode2 = link12->second;
3305           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3306             Ok12 = true;
3307         }
3308         if(tr3) {
3309           linkNode1 = link13->first;
3310           linkNode2 = link13->second;
3311           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3312             Ok13 = true;
3313         }
3314
3315         // Choose a pair to fuse
3316         if ( Ok12 && Ok13 ) {
3317           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3318           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3319           double aBadRate12 = getBadRate( &quad12, theCrit );
3320           double aBadRate13 = getBadRate( &quad13, theCrit );
3321           if (  aBadRate13 < aBadRate12 )
3322             Ok12 = false;
3323           else
3324             Ok13 = false;
3325         }
3326
3327         // Make quadrangles
3328         // and remove fused elems and remove links from the maps
3329         mapEl_setLi.erase( tr1 );
3330         if ( Ok12 )
3331         {
3332           mapEl_setLi.erase( tr2 );
3333           mapLi_listEl.erase( *link12 );
3334           if ( tr1->NbNodes() == 3 )
3335           {
3336             const SMDS_MeshElement* newElem = 0;
3337             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3338             myLastCreatedElems.Append(newElem);
3339             AddToSameGroups( newElem, tr1, aMesh );
3340             int aShapeId = tr1->getshapeId();
3341             if ( aShapeId )
3342               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3343             aMesh->RemoveElement( tr1 );
3344             aMesh->RemoveElement( tr2 );
3345           }
3346           else {
3347             vector< const SMDS_MeshNode* > N1;
3348             vector< const SMDS_MeshNode* > N2;
3349             getNodesFromTwoTria(tr1,tr2,N1,N2);
3350             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3351             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3352             // i.e. first nodes from both arrays form a new diagonal
3353             const SMDS_MeshNode* aNodes[8];
3354             aNodes[0] = N1[0];
3355             aNodes[1] = N1[1];
3356             aNodes[2] = N2[0];
3357             aNodes[3] = N2[1];
3358             aNodes[4] = N1[3];
3359             aNodes[5] = N2[5];
3360             aNodes[6] = N2[3];
3361             aNodes[7] = N1[5];
3362             const SMDS_MeshElement* newElem = 0;
3363             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3364               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3365                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3366             else
3367               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3368                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3369             myLastCreatedElems.Append(newElem);
3370             AddToSameGroups( newElem, tr1, aMesh );
3371             int aShapeId = tr1->getshapeId();
3372             if ( aShapeId )
3373               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3374             aMesh->RemoveElement( tr1 );
3375             aMesh->RemoveElement( tr2 );
3376             // remove middle node (9)
3377             if ( N1[4]->NbInverseElements() == 0 )
3378               aMesh->RemoveNode( N1[4] );
3379             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3380               aMesh->RemoveNode( N1[6] );
3381             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3382               aMesh->RemoveNode( N2[6] );
3383           }
3384         }
3385         else if ( Ok13 )
3386         {
3387           mapEl_setLi.erase( tr3 );
3388           mapLi_listEl.erase( *link13 );
3389           if ( tr1->NbNodes() == 3 ) {
3390             const SMDS_MeshElement* newElem = 0;
3391             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3392             myLastCreatedElems.Append(newElem);
3393             AddToSameGroups( newElem, tr1, aMesh );
3394             int aShapeId = tr1->getshapeId();
3395             if ( aShapeId )
3396               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3397             aMesh->RemoveElement( tr1 );
3398             aMesh->RemoveElement( tr3 );
3399           }
3400           else {
3401             vector< const SMDS_MeshNode* > N1;
3402             vector< const SMDS_MeshNode* > N2;
3403             getNodesFromTwoTria(tr1,tr3,N1,N2);
3404             // now we receive following N1 and N2 (using numeration as above image)
3405             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3406             // i.e. first nodes from both arrays form a new diagonal
3407             const SMDS_MeshNode* aNodes[8];
3408             aNodes[0] = N1[0];
3409             aNodes[1] = N1[1];
3410             aNodes[2] = N2[0];
3411             aNodes[3] = N2[1];
3412             aNodes[4] = N1[3];
3413             aNodes[5] = N2[5];
3414             aNodes[6] = N2[3];
3415             aNodes[7] = N1[5];
3416             const SMDS_MeshElement* newElem = 0;
3417             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3418               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3419                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3420             else
3421               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3422                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3423             myLastCreatedElems.Append(newElem);
3424             AddToSameGroups( newElem, tr1, aMesh );
3425             int aShapeId = tr1->getshapeId();
3426             if ( aShapeId )
3427               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3428             aMesh->RemoveElement( tr1 );
3429             aMesh->RemoveElement( tr3 );
3430             // remove middle node (9)
3431             if ( N1[4]->NbInverseElements() == 0 )
3432               aMesh->RemoveNode( N1[4] );
3433             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3434               aMesh->RemoveNode( N1[6] );
3435             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3436               aMesh->RemoveNode( N2[6] );
3437           }
3438         }
3439
3440         // Next element to fuse: the rejected one
3441         if ( tr3 )
3442           startElem = Ok12 ? tr3 : tr2;
3443
3444       } // if ( startElem )
3445     } // while ( startElem || !startLinks.empty() )
3446   } // while ( ! mapEl_setLi.empty() )
3447
3448   return true;
3449 }
3450
3451
3452 /*#define DUMPSO(txt) \
3453 //  cout << txt << endl;
3454 //=============================================================================
3455 //
3456 //
3457 //
3458 //=============================================================================
3459 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3460 {
3461 if ( i1 == i2 )
3462 return;
3463 int tmp = idNodes[ i1 ];
3464 idNodes[ i1 ] = idNodes[ i2 ];
3465 idNodes[ i2 ] = tmp;
3466 gp_Pnt Ptmp = P[ i1 ];
3467 P[ i1 ] = P[ i2 ];
3468 P[ i2 ] = Ptmp;
3469 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3470 }
3471
3472 //=======================================================================
3473 //function : SortQuadNodes
3474 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3475 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3476 //           1 or 2 else 0.
3477 //=======================================================================
3478
3479 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3480 int               idNodes[] )
3481 {
3482   gp_Pnt P[4];
3483   int i;
3484   for ( i = 0; i < 4; i++ ) {
3485     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3486     if ( !n ) return 0;
3487     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3488   }
3489
3490   gp_Vec V1(P[0], P[1]);
3491   gp_Vec V2(P[0], P[2]);
3492   gp_Vec V3(P[0], P[3]);
3493
3494   gp_Vec Cross1 = V1 ^ V2;
3495   gp_Vec Cross2 = V2 ^ V3;
3496
3497   i = 0;
3498   if (Cross1.Dot(Cross2) < 0)
3499   {
3500     Cross1 = V2 ^ V1;
3501     Cross2 = V1 ^ V3;
3502
3503     if (Cross1.Dot(Cross2) < 0)
3504       i = 2;
3505     else
3506       i = 1;
3507     swap ( i, i + 1, idNodes, P );
3508
3509     //     for ( int ii = 0; ii < 4; ii++ ) {
3510     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3511     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3512     //     }
3513   }
3514   return i;
3515 }
3516
3517 //=======================================================================
3518 //function : SortHexaNodes
3519 //purpose  : Set 8 nodes of a hexahedron in a good order.
3520 //           Return success status
3521 //=======================================================================
3522
3523 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3524                                       int               idNodes[] )
3525 {
3526   gp_Pnt P[8];
3527   int i;
3528   DUMPSO( "INPUT: ========================================");
3529   for ( i = 0; i < 8; i++ ) {
3530     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3531     if ( !n ) return false;
3532     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3533     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3534   }
3535   DUMPSO( "========================================");
3536
3537
3538   set<int> faceNodes;  // ids of bottom face nodes, to be found
3539   set<int> checkedId1; // ids of tried 2-nd nodes
3540   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3541   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3542   int iMin, iLoop1 = 0;
3543
3544   // Loop to try the 2-nd nodes
3545
3546   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3547   {
3548     // Find not checked 2-nd node
3549     for ( i = 1; i < 8; i++ )
3550       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3551         int id1 = idNodes[i];
3552         swap ( 1, i, idNodes, P );
3553         checkedId1.insert ( id1 );
3554         break;
3555       }
3556
3557     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3558     // ie that all but meybe one (id3 which is on the same face) nodes
3559     // lay on the same side from the triangle plane.
3560
3561     bool manyInPlane = false; // more than 4 nodes lay in plane
3562     int iLoop2 = 0;
3563     while ( ++iLoop2 < 6 ) {
3564
3565       // get 1-2-3 plane coeffs
3566       Standard_Real A, B, C, D;
3567       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3568       if ( N.SquareMagnitude() > gp::Resolution() )
3569       {
3570         gp_Pln pln ( P[0], N );
3571         pln.Coefficients( A, B, C, D );
3572
3573         // find the node (iMin) closest to pln
3574         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3575         set<int> idInPln;
3576         for ( i = 3; i < 8; i++ ) {
3577           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3578           if ( fabs( dist[i] ) < minDist ) {
3579             minDist = fabs( dist[i] );
3580             iMin = i;
3581           }
3582           if ( fabs( dist[i] ) <= tol )
3583             idInPln.insert( idNodes[i] );
3584         }
3585
3586         // there should not be more than 4 nodes in bottom plane
3587         if ( idInPln.size() > 1 )
3588         {
3589           DUMPSO( "### idInPln.size() = " << idInPln.size());
3590           // idInPlane does not contain the first 3 nodes
3591           if ( manyInPlane || idInPln.size() == 5)
3592             return false; // all nodes in one plane
3593           manyInPlane = true;
3594
3595           // set the 1-st node to be not in plane
3596           for ( i = 3; i < 8; i++ ) {
3597             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3598               DUMPSO( "### Reset 0-th node");
3599               swap( 0, i, idNodes, P );
3600               break;
3601             }
3602           }
3603
3604           // reset to re-check second nodes
3605           leastDist = DBL_MAX;
3606           faceNodes.clear();
3607           checkedId1.clear();
3608           iLoop1 = 0;
3609           break; // from iLoop2;
3610         }
3611
3612         // check that the other 4 nodes are on the same side
3613         bool sameSide = true;
3614         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3615         for ( i = 3; sameSide && i < 8; i++ ) {
3616           if ( i != iMin )
3617             sameSide = ( isNeg == dist[i] <= 0.);
3618         }
3619
3620         // keep best solution
3621         if ( sameSide && minDist < leastDist ) {
3622           leastDist = minDist;
3623           faceNodes.clear();
3624           faceNodes.insert( idNodes[ 1 ] );
3625           faceNodes.insert( idNodes[ 2 ] );
3626           faceNodes.insert( idNodes[ iMin ] );
3627           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3628                   << " leastDist = " << leastDist);
3629           if ( leastDist <= DBL_MIN )
3630             break;
3631         }
3632       }
3633
3634       // set next 3-d node to check
3635       int iNext = 2 + iLoop2;
3636       if ( iNext < 8 ) {
3637         DUMPSO( "Try 2-nd");
3638         swap ( 2, iNext, idNodes, P );
3639       }
3640     } // while ( iLoop2 < 6 )
3641   } // iLoop1
3642
3643   if ( faceNodes.empty() ) return false;
3644
3645   // Put the faceNodes in proper places
3646   for ( i = 4; i < 8; i++ ) {
3647     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3648       // find a place to put
3649       int iTo = 1;
3650       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3651         iTo++;
3652       DUMPSO( "Set faceNodes");
3653       swap ( iTo, i, idNodes, P );
3654     }
3655   }
3656
3657
3658   // Set nodes of the found bottom face in good order
3659   DUMPSO( " Found bottom face: ");
3660   i = SortQuadNodes( theMesh, idNodes );
3661   if ( i ) {
3662     gp_Pnt Ptmp = P[ i ];
3663     P[ i ] = P[ i+1 ];
3664     P[ i+1 ] = Ptmp;
3665   }
3666   //   else
3667   //     for ( int ii = 0; ii < 4; ii++ ) {
3668   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3669   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3670   //    }
3671
3672   // Gravity center of the top and bottom faces
3673   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3674   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3675
3676   // Get direction from the bottom to the top face
3677   gp_Vec upDir ( aGCb, aGCt );
3678   Standard_Real upDirSize = upDir.Magnitude();
3679   if ( upDirSize <= gp::Resolution() ) return false;
3680   upDir / upDirSize;
3681
3682   // Assure that the bottom face normal points up
3683   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3684   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3685   if ( Nb.Dot( upDir ) < 0 ) {
3686     DUMPSO( "Reverse bottom face");
3687     swap( 1, 3, idNodes, P );
3688   }
3689
3690   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3691   Standard_Real minDist = DBL_MAX;
3692   for ( i = 4; i < 8; i++ ) {
3693     // projection of P[i] to the plane defined by P[0] and upDir
3694     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3695     Standard_Real sqDist = P[0].SquareDistance( Pp );
3696     if ( sqDist < minDist ) {
3697       minDist = sqDist;
3698       iMin = i;
3699     }
3700   }
3701   DUMPSO( "Set 4-th");
3702   swap ( 4, iMin, idNodes, P );
3703
3704   // Set nodes of the top face in good order
3705   DUMPSO( "Sort top face");
3706   i = SortQuadNodes( theMesh, &idNodes[4] );
3707   if ( i ) {
3708     i += 4;
3709     gp_Pnt Ptmp = P[ i ];
3710     P[ i ] = P[ i+1 ];
3711     P[ i+1 ] = Ptmp;
3712   }
3713
3714   // Assure that direction of the top face normal is from the bottom face
3715   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3716   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3717   if ( Nt.Dot( upDir ) < 0 ) {
3718     DUMPSO( "Reverse top face");
3719     swap( 5, 7, idNodes, P );
3720   }
3721
3722   //   DUMPSO( "OUTPUT: ========================================");
3723   //   for ( i = 0; i < 8; i++ ) {
3724   //     float *p = ugrid->GetPoint(idNodes[i]);
3725   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3726   //   }
3727
3728   return true;
3729 }*/
3730
3731 //================================================================================
3732 /*!
3733  * \brief Return nodes linked to the given one
3734  * \param theNode - the node
3735  * \param linkedNodes - the found nodes
3736  * \param type - the type of elements to check
3737  *
3738  * Medium nodes are ignored
3739  */
3740 //================================================================================
3741
3742 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3743                                        TIDSortedElemSet &   linkedNodes,
3744                                        SMDSAbs_ElementType  type )
3745 {
3746   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3747   while ( elemIt->more() )
3748   {
3749     const SMDS_MeshElement* elem = elemIt->next();
3750     if(elem->GetType() == SMDSAbs_0DElement)
3751       continue;
3752
3753     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3754     if ( elem->GetType() == SMDSAbs_Volume )
3755     {
3756       SMDS_VolumeTool vol( elem );
3757       while ( nodeIt->more() ) {
3758         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3759         if ( theNode != n && vol.IsLinked( theNode, n ))
3760           linkedNodes.insert( n );
3761       }
3762     }
3763     else
3764     {
3765       for ( int i = 0; nodeIt->more(); ++i ) {
3766         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3767         if ( n == theNode ) {
3768           int iBefore = i - 1;
3769           int iAfter  = i + 1;
3770           if ( elem->IsQuadratic() ) {
3771             int nb = elem->NbNodes() / 2;
3772             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3773             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3774           }
3775           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3776           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3777         }
3778       }
3779     }
3780   }
3781 }
3782
3783 //=======================================================================
3784 //function : laplacianSmooth
3785 //purpose  : pulls theNode toward the center of surrounding nodes directly
3786 //           connected to that node along an element edge
3787 //=======================================================================
3788
3789 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3790                      const Handle(Geom_Surface)&          theSurface,
3791                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3792 {
3793   // find surrounding nodes
3794
3795   TIDSortedElemSet nodeSet;
3796   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3797
3798   // compute new coodrs
3799
3800   double coord[] = { 0., 0., 0. };
3801   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3802   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3803     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3804     if ( theSurface.IsNull() ) { // smooth in 3D
3805       coord[0] += node->X();
3806       coord[1] += node->Y();
3807       coord[2] += node->Z();
3808     }
3809     else { // smooth in 2D
3810       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3811       gp_XY* uv = theUVMap[ node ];
3812       coord[0] += uv->X();
3813       coord[1] += uv->Y();
3814     }
3815   }
3816   int nbNodes = nodeSet.size();
3817   if ( !nbNodes )
3818     return;
3819   coord[0] /= nbNodes;
3820   coord[1] /= nbNodes;
3821
3822   if ( !theSurface.IsNull() ) {
3823     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3824     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3825     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3826     coord[0] = p3d.X();
3827     coord[1] = p3d.Y();
3828     coord[2] = p3d.Z();
3829   }
3830   else
3831     coord[2] /= nbNodes;
3832
3833   // move node
3834
3835   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3836 }
3837
3838 //=======================================================================
3839 //function : centroidalSmooth
3840 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3841 //           surrounding elements
3842 //=======================================================================
3843
3844 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3845                       const Handle(Geom_Surface)&          theSurface,
3846                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3847 {
3848   gp_XYZ aNewXYZ(0.,0.,0.);
3849   SMESH::Controls::Area anAreaFunc;
3850   double totalArea = 0.;
3851   int nbElems = 0;
3852
3853   // compute new XYZ
3854
3855   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3856   while ( elemIt->more() )
3857   {
3858     const SMDS_MeshElement* elem = elemIt->next();
3859     nbElems++;
3860
3861     gp_XYZ elemCenter(0.,0.,0.);
3862     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3863     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3864     int nn = elem->NbNodes();
3865     if(elem->IsQuadratic()) nn = nn/2;
3866     int i=0;
3867     //while ( itN->more() ) {
3868     while ( i<nn ) {
3869       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3870       i++;
3871       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3872       aNodePoints.push_back( aP );
3873       if ( !theSurface.IsNull() ) { // smooth in 2D
3874         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3875         gp_XY* uv = theUVMap[ aNode ];
3876         aP.SetCoord( uv->X(), uv->Y(), 0. );
3877       }
3878       elemCenter += aP;
3879     }
3880     double elemArea = anAreaFunc.GetValue( aNodePoints );
3881     totalArea += elemArea;
3882     elemCenter /= nn;
3883     aNewXYZ += elemCenter * elemArea;
3884   }
3885   aNewXYZ /= totalArea;
3886   if ( !theSurface.IsNull() ) {
3887     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3888     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3889   }
3890
3891   // move node
3892
3893   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3894 }
3895
3896 //=======================================================================
3897 //function : getClosestUV
3898 //purpose  : return UV of closest projection
3899 //=======================================================================
3900
3901 static bool getClosestUV (Extrema_GenExtPS& projector,
3902                           const gp_Pnt&     point,
3903                           gp_XY &           result)
3904 {
3905   projector.Perform( point );
3906   if ( projector.IsDone() ) {
3907     double u, v, minVal = DBL_MAX;
3908     for ( int i = projector.NbExt(); i > 0; i-- )
3909       if ( projector.SquareDistance( i ) < minVal ) {
3910         minVal = projector.SquareDistance( i );
3911         projector.Point( i ).Parameter( u, v );
3912       }
3913     result.SetCoord( u, v );
3914     return true;
3915   }
3916   return false;
3917 }
3918
3919 //=======================================================================
3920 //function : Smooth
3921 //purpose  : Smooth theElements during theNbIterations or until a worst
3922 //           element has aspect ratio <= theTgtAspectRatio.
3923 //           Aspect Ratio varies in range [1.0, inf].
3924 //           If theElements is empty, the whole mesh is smoothed.
3925 //           theFixedNodes contains additionally fixed nodes. Nodes built
3926 //           on edges and boundary nodes are always fixed.
3927 //=======================================================================
3928
3929 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3930                                set<const SMDS_MeshNode*> & theFixedNodes,
3931                                const SmoothMethod          theSmoothMethod,
3932                                const int                   theNbIterations,
3933                                double                      theTgtAspectRatio,
3934                                const bool                  the2D)
3935 {
3936   myLastCreatedElems.Clear();
3937   myLastCreatedNodes.Clear();
3938
3939   if ( theTgtAspectRatio < 1.0 )
3940     theTgtAspectRatio = 1.0;
3941
3942   const double disttol = 1.e-16;
3943
3944   SMESH::Controls::AspectRatio aQualityFunc;
3945
3946   SMESHDS_Mesh* aMesh = GetMeshDS();
3947
3948   if ( theElems.empty() ) {
3949     // add all faces to theElems
3950     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3951     while ( fIt->more() ) {
3952       const SMDS_MeshElement* face = fIt->next();
3953       theElems.insert( theElems.end(), face );
3954     }
3955   }
3956   // get all face ids theElems are on
3957   set< int > faceIdSet;
3958   TIDSortedElemSet::iterator itElem;
3959   if ( the2D )
3960     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3961       int fId = FindShape( *itElem );
3962       // check that corresponding submesh exists and a shape is face
3963       if (fId &&
3964           faceIdSet.find( fId ) == faceIdSet.end() &&
3965           aMesh->MeshElements( fId )) {
3966         TopoDS_Shape F = aMesh->IndexToShape( fId );
3967         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3968           faceIdSet.insert( fId );
3969       }
3970     }
3971   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3972
3973   // ===============================================
3974   // smooth elements on each TopoDS_Face separately
3975   // ===============================================
3976
3977   SMESH_MesherHelper helper( *GetMesh() );
3978
3979   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3980   for ( ; fId != faceIdSet.rend(); ++fId )
3981   {
3982     // get face surface and submesh
3983     Handle(Geom_Surface) surface;
3984     SMESHDS_SubMesh* faceSubMesh = 0;
3985     TopoDS_Face face;
3986     double fToler2 = 0;
3987     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3988     bool isUPeriodic = false, isVPeriodic = false;
3989     if ( *fId )
3990     {
3991       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3992       surface = BRep_Tool::Surface( face );
3993       faceSubMesh = aMesh->MeshElements( *fId );
3994       fToler2 = BRep_Tool::Tolerance( face );
3995       fToler2 *= fToler2 * 10.;
3996       isUPeriodic = surface->IsUPeriodic();
3997       // if ( isUPeriodic )
3998       //   surface->UPeriod();
3999       isVPeriodic = surface->IsVPeriodic();
4000       // if ( isVPeriodic )
4001       //   surface->VPeriod();
4002       surface->Bounds( u1, u2, v1, v2 );
4003       helper.SetSubShape( face );
4004     }
4005     // ---------------------------------------------------------
4006     // for elements on a face, find movable and fixed nodes and
4007     // compute UV for them
4008     // ---------------------------------------------------------
4009     bool checkBoundaryNodes = false;
4010     bool isQuadratic = false;
4011     set<const SMDS_MeshNode*> setMovableNodes;
4012     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4013     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4014     list< const SMDS_MeshElement* > elemsOnFace;
4015
4016     Extrema_GenExtPS projector;
4017     GeomAdaptor_Surface surfAdaptor;
4018     if ( !surface.IsNull() ) {
4019       surfAdaptor.Load( surface );
4020       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4021     }
4022     int nbElemOnFace = 0;
4023     itElem = theElems.begin();
4024     // loop on not yet smoothed elements: look for elems on a face
4025     while ( itElem != theElems.end() )
4026     {
4027       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4028         break; // all elements found
4029
4030       const SMDS_MeshElement* elem = *itElem;
4031       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4032            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4033         ++itElem;
4034         continue;
4035       }
4036       elemsOnFace.push_back( elem );
4037       theElems.erase( itElem++ );
4038       nbElemOnFace++;
4039
4040       if ( !isQuadratic )
4041         isQuadratic = elem->IsQuadratic();
4042
4043       // get movable nodes of elem
4044       const SMDS_MeshNode* node;
4045       SMDS_TypeOfPosition posType;
4046       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4047       int nn = 0, nbn =  elem->NbNodes();
4048       if(elem->IsQuadratic())
4049         nbn = nbn/2;
4050       while ( nn++ < nbn ) {
4051         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4052         const SMDS_PositionPtr& pos = node->GetPosition();
4053         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4054         if (posType != SMDS_TOP_EDGE &&
4055             posType != SMDS_TOP_VERTEX &&
4056             theFixedNodes.find( node ) == theFixedNodes.end())
4057         {
4058           // check if all faces around the node are on faceSubMesh
4059           // because a node on edge may be bound to face
4060           bool all = true;
4061           if ( faceSubMesh ) {
4062             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4063             while ( eIt->more() && all ) {
4064               const SMDS_MeshElement* e = eIt->next();
4065               all = faceSubMesh->Contains( e );
4066             }
4067           }
4068           if ( all )
4069             setMovableNodes.insert( node );
4070           else
4071             checkBoundaryNodes = true;
4072         }
4073         if ( posType == SMDS_TOP_3DSPACE )
4074           checkBoundaryNodes = true;
4075       }
4076
4077       if ( surface.IsNull() )
4078         continue;
4079
4080       // get nodes to check UV
4081       list< const SMDS_MeshNode* > uvCheckNodes;
4082       const SMDS_MeshNode* nodeInFace = 0;
4083       itN = elem->nodesIterator();
4084       nn = 0; nbn =  elem->NbNodes();
4085       if(elem->IsQuadratic())
4086         nbn = nbn/2;
4087       while ( nn++ < nbn ) {
4088         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4089         if ( node->GetPosition()->GetDim() == 2 )
4090           nodeInFace = node;
4091         if ( uvMap.find( node ) == uvMap.end() )
4092           uvCheckNodes.push_back( node );
4093         // add nodes of elems sharing node
4094         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4095         //         while ( eIt->more() ) {
4096         //           const SMDS_MeshElement* e = eIt->next();
4097         //           if ( e != elem ) {
4098         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4099         //             while ( nIt->more() ) {
4100         //               const SMDS_MeshNode* n =
4101         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4102         //               if ( uvMap.find( n ) == uvMap.end() )
4103         //                 uvCheckNodes.push_back( n );
4104         //             }
4105         //           }
4106         //         }
4107       }
4108       // check UV on face
4109       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4110       for ( ; n != uvCheckNodes.end(); ++n ) {
4111         node = *n;
4112         gp_XY uv( 0, 0 );
4113         const SMDS_PositionPtr& pos = node->GetPosition();
4114         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4115         // get existing UV
4116         if ( pos )
4117         {
4118           bool toCheck = true;
4119           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4120         }
4121         // compute not existing UV
4122         bool project = ( posType == SMDS_TOP_3DSPACE );
4123         // double dist1 = DBL_MAX, dist2 = 0;
4124         // if ( posType != SMDS_TOP_3DSPACE ) {
4125         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4126         //   project = dist1 > fToler2;
4127         // }
4128         if ( project ) { // compute new UV
4129           gp_XY newUV;
4130           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4131           if ( !getClosestUV( projector, pNode, newUV )) {
4132             MESSAGE("Node Projection Failed " << node);
4133           }
4134           else {
4135             if ( isUPeriodic )
4136               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4137             if ( isVPeriodic )
4138               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4139             // check new UV
4140             // if ( posType != SMDS_TOP_3DSPACE )
4141             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4142             // if ( dist2 < dist1 )
4143               uv = newUV;
4144           }
4145         }
4146         // store UV in the map
4147         listUV.push_back( uv );
4148         uvMap.insert( make_pair( node, &listUV.back() ));
4149       }
4150     } // loop on not yet smoothed elements
4151
4152     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4153       checkBoundaryNodes = true;
4154
4155     // fix nodes on mesh boundary
4156
4157     if ( checkBoundaryNodes ) {
4158       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4159       map< SMESH_TLink, int >::iterator link_nb;
4160       // put all elements links to linkNbMap
4161       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4162       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4163         const SMDS_MeshElement* elem = (*elemIt);
4164         int nbn =  elem->NbCornerNodes();
4165         // loop on elem links: insert them in linkNbMap
4166         for ( int iN = 0; iN < nbn; ++iN ) {
4167           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4168           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4169           SMESH_TLink link( n1, n2 );
4170           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4171           link_nb->second++;
4172         }
4173       }
4174       // remove nodes that are in links encountered only once from setMovableNodes
4175       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4176         if ( link_nb->second == 1 ) {
4177           setMovableNodes.erase( link_nb->first.node1() );
4178           setMovableNodes.erase( link_nb->first.node2() );
4179         }
4180       }
4181     }
4182
4183     // -----------------------------------------------------
4184     // for nodes on seam edge, compute one more UV ( uvMap2 );
4185     // find movable nodes linked to nodes on seam and which
4186     // are to be smoothed using the second UV ( uvMap2 )
4187     // -----------------------------------------------------
4188
4189     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4190     if ( !surface.IsNull() ) {
4191       TopExp_Explorer eExp( face, TopAbs_EDGE );
4192       for ( ; eExp.More(); eExp.Next() ) {
4193         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4194         if ( !BRep_Tool::IsClosed( edge, face ))
4195           continue;
4196         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4197         if ( !sm ) continue;
4198         // find out which parameter varies for a node on seam
4199         double f,l;
4200         gp_Pnt2d uv1, uv2;
4201         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4202         if ( pcurve.IsNull() ) continue;
4203         uv1 = pcurve->Value( f );
4204         edge.Reverse();
4205         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206         if ( pcurve.IsNull() ) continue;
4207         uv2 = pcurve->Value( f );
4208         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4209         // assure uv1 < uv2
4210         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4211           std::swap( uv1, uv2 );
4212         // get nodes on seam and its vertices
4213         list< const SMDS_MeshNode* > seamNodes;
4214         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4215         while ( nSeamIt->more() ) {
4216           const SMDS_MeshNode* node = nSeamIt->next();
4217           if ( !isQuadratic || !IsMedium( node ))
4218             seamNodes.push_back( node );
4219         }
4220         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4221         for ( ; vExp.More(); vExp.Next() ) {
4222           sm = aMesh->MeshElements( vExp.Current() );
4223           if ( sm ) {
4224             nSeamIt = sm->GetNodes();
4225             while ( nSeamIt->more() )
4226               seamNodes.push_back( nSeamIt->next() );
4227           }
4228         }
4229         // loop on nodes on seam
4230         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4231         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4232           const SMDS_MeshNode* nSeam = *noSeIt;
4233           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4234           if ( n_uv == uvMap.end() )
4235             continue;
4236           // set the first UV
4237           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4238           // set the second UV
4239           listUV.push_back( *n_uv->second );
4240           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4241           if ( uvMap2.empty() )
4242             uvMap2 = uvMap; // copy the uvMap contents
4243           uvMap2[ nSeam ] = &listUV.back();
4244
4245           // collect movable nodes linked to ones on seam in nodesNearSeam
4246           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4247           while ( eIt->more() ) {
4248             const SMDS_MeshElement* e = eIt->next();
4249             int nbUseMap1 = 0, nbUseMap2 = 0;
4250             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4251             int nn = 0, nbn =  e->NbNodes();
4252             if(e->IsQuadratic()) nbn = nbn/2;
4253             while ( nn++ < nbn )
4254             {
4255               const SMDS_MeshNode* n =
4256                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4257               if (n == nSeam ||
4258                   setMovableNodes.find( n ) == setMovableNodes.end() )
4259                 continue;
4260               // add only nodes being closer to uv2 than to uv1
4261               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4262               //              0.5 * ( n->Y() + nSeam->Y() ),
4263               //              0.5 * ( n->Z() + nSeam->Z() ));
4264               // gp_XY uv;
4265               // getClosestUV( projector, pMid, uv );
4266               double x = uvMap[ n ]->Coord( iPar );
4267               if ( Abs( uv1.Coord( iPar ) - x ) >
4268                    Abs( uv2.Coord( iPar ) - x )) {
4269                 nodesNearSeam.insert( n );
4270                 nbUseMap2++;
4271               }
4272               else
4273                 nbUseMap1++;
4274             }
4275             // for centroidalSmooth all element nodes must
4276             // be on one side of a seam
4277             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4278               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4279               nn = 0;
4280               while ( nn++ < nbn ) {
4281                 const SMDS_MeshNode* n =
4282                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4283                 setMovableNodes.erase( n );
4284               }
4285             }
4286           }
4287         } // loop on nodes on seam
4288       } // loop on edge of a face
4289     } // if ( !face.IsNull() )
4290
4291     if ( setMovableNodes.empty() ) {
4292       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4293       continue; // goto next face
4294     }
4295
4296     // -------------
4297     // SMOOTHING //
4298     // -------------
4299
4300     int it = -1;
4301     double maxRatio = -1., maxDisplacement = -1.;
4302     set<const SMDS_MeshNode*>::iterator nodeToMove;
4303     for ( it = 0; it < theNbIterations; it++ ) {
4304       maxDisplacement = 0.;
4305       nodeToMove = setMovableNodes.begin();
4306       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4307         const SMDS_MeshNode* node = (*nodeToMove);
4308         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4309
4310         // smooth
4311         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4312         if ( theSmoothMethod == LAPLACIAN )
4313           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4314         else
4315           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4316
4317         // node displacement
4318         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4319         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4320         if ( aDispl > maxDisplacement )
4321           maxDisplacement = aDispl;
4322       }
4323       // no node movement => exit
4324       //if ( maxDisplacement < 1.e-16 ) {
4325       if ( maxDisplacement < disttol ) {
4326         MESSAGE("-- no node movement --");
4327         break;
4328       }
4329
4330       // check elements quality
4331       maxRatio  = 0;
4332       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4333       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4334         const SMDS_MeshElement* elem = (*elemIt);
4335         if ( !elem || elem->GetType() != SMDSAbs_Face )
4336           continue;
4337         SMESH::Controls::TSequenceOfXYZ aPoints;
4338         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4339           double aValue = aQualityFunc.GetValue( aPoints );
4340           if ( aValue > maxRatio )
4341             maxRatio = aValue;
4342         }
4343       }
4344       if ( maxRatio <= theTgtAspectRatio ) {
4345         //MESSAGE("-- quality achieved --");
4346         break;
4347       }
4348       if (it+1 == theNbIterations) {
4349         //MESSAGE("-- Iteration limit exceeded --");
4350       }
4351     } // smoothing iterations
4352
4353     // MESSAGE(" Face id: " << *fId <<
4354     //         " Nb iterstions: " << it <<
4355     //         " Displacement: " << maxDisplacement <<
4356     //         " Aspect Ratio " << maxRatio);
4357
4358     // ---------------------------------------
4359     // new nodes positions are computed,
4360     // record movement in DS and set new UV
4361     // ---------------------------------------
4362     nodeToMove = setMovableNodes.begin();
4363     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4364       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4365       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4366       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4367       if ( node_uv != uvMap.end() ) {
4368         gp_XY* uv = node_uv->second;
4369         node->SetPosition
4370           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4371       }
4372     }
4373
4374     // move medium nodes of quadratic elements
4375     if ( isQuadratic )
4376     {
4377       vector<const SMDS_MeshNode*> nodes;
4378       bool checkUV;
4379       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4380       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4381       {
4382         const SMDS_MeshElement* QF = *elemIt;
4383         if ( QF->IsQuadratic() )
4384         {
4385           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4386                         SMDS_MeshElement::iterator() );
4387           nodes.push_back( nodes[0] );
4388           gp_Pnt xyz;
4389           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4390           {
4391             if ( !surface.IsNull() )
4392             {
4393               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4394               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4395               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4396               xyz = surface->Value( uv.X(), uv.Y() );
4397             }
4398             else {
4399               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4400             }
4401             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4402               // we have to move a medium node
4403               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4404           }
4405         }
4406       }
4407     }
4408
4409   } // loop on face ids
4410
4411 }
4412
4413 namespace
4414 {
4415   //=======================================================================
4416   //function : isReverse
4417   //purpose  : Return true if normal of prevNodes is not co-directied with
4418   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4419   //           iNotSame is where prevNodes and nextNodes are different.
4420   //           If result is true then future volume orientation is OK
4421   //=======================================================================
4422
4423   bool isReverse(const SMDS_MeshElement*             face,
4424                  const vector<const SMDS_MeshNode*>& prevNodes,
4425                  const vector<const SMDS_MeshNode*>& nextNodes,
4426                  const int                           iNotSame)
4427   {
4428
4429     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4430     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4431     gp_XYZ extrDir( pN - pP ), faceNorm;
4432     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4433
4434     return faceNorm * extrDir < 0.0;
4435   }
4436
4437   //================================================================================
4438   /*!
4439    * \brief Assure that theElemSets[0] holds elements, not nodes
4440    */
4441   //================================================================================
4442
4443   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4444   {
4445     if ( !theElemSets[0].empty() &&
4446          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4447     {
4448       std::swap( theElemSets[0], theElemSets[1] );
4449     }
4450     else if ( !theElemSets[1].empty() &&
4451               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4452     {
4453       std::swap( theElemSets[0], theElemSets[1] );
4454     }
4455   }
4456 }
4457
4458 //=======================================================================
4459 /*!
4460  * \brief Create elements by sweeping an element
4461  * \param elem - element to sweep
4462  * \param newNodesItVec - nodes generated from each node of the element
4463  * \param newElems - generated elements
4464  * \param nbSteps - number of sweeping steps
4465  * \param srcElements - to append elem for each generated element
4466  */
4467 //=======================================================================
4468
4469 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4470                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4471                                     list<const SMDS_MeshElement*>&        newElems,
4472                                     const size_t                          nbSteps,
4473                                     SMESH_SequenceOfElemPtr&              srcElements)
4474 {
4475   SMESHDS_Mesh* aMesh = GetMeshDS();
4476
4477   const int           nbNodes = elem->NbNodes();
4478   const int         nbCorners = elem->NbCornerNodes();
4479   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4480                                                           polyhedron creation !!! */
4481   // Loop on elem nodes:
4482   // find new nodes and detect same nodes indices
4483   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4484   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4485   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4486   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4487
4488   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4489   vector<int> sames(nbNodes);
4490   vector<bool> isSingleNode(nbNodes);
4491
4492   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4493     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4494     const SMDS_MeshNode*                         node = nnIt->first;
4495     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4496     if ( listNewNodes.empty() )
4497       return;
4498
4499     itNN   [ iNode ] = listNewNodes.begin();
4500     prevNod[ iNode ] = node;
4501     nextNod[ iNode ] = listNewNodes.front();
4502
4503     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4504                                                              corner node of linear */
4505     if ( prevNod[ iNode ] != nextNod [ iNode ])
4506       nbDouble += !isSingleNode[iNode];
4507
4508     if( iNode < nbCorners ) { // check corners only
4509       if ( prevNod[ iNode ] == nextNod [ iNode ])
4510         sames[nbSame++] = iNode;
4511       else
4512         iNotSameNode = iNode;
4513     }
4514   }
4515
4516   if ( nbSame == nbNodes || nbSame > 2) {
4517     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4518     return;
4519   }
4520
4521   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4522   {
4523     // fix nodes order to have bottom normal external
4524     if ( baseType == SMDSEntity_Polygon )
4525     {
4526       std::reverse( itNN.begin(), itNN.end() );
4527       std::reverse( prevNod.begin(), prevNod.end() );
4528       std::reverse( midlNod.begin(), midlNod.end() );
4529       std::reverse( nextNod.begin(), nextNod.end() );
4530       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4531     }
4532     else
4533     {
4534       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4535       SMDS_MeshCell::applyInterlace( ind, itNN );
4536       SMDS_MeshCell::applyInterlace( ind, prevNod );
4537       SMDS_MeshCell::applyInterlace( ind, nextNod );
4538       SMDS_MeshCell::applyInterlace( ind, midlNod );
4539       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4540       if ( nbSame > 0 )
4541       {
4542         sames[nbSame] = iNotSameNode;
4543         for ( int j = 0; j <= nbSame; ++j )
4544           for ( size_t i = 0; i < ind.size(); ++i )
4545             if ( ind[i] == sames[j] )
4546             {
4547               sames[j] = i;
4548               break;
4549             }
4550         iNotSameNode = sames[nbSame];
4551       }
4552     }
4553   }
4554   else if ( elem->GetType() == SMDSAbs_Edge )
4555   {
4556     // orient a new face same as adjacent one
4557     int i1, i2;
4558     const SMDS_MeshElement* e;
4559     TIDSortedElemSet dummy;
4560     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4561         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4562         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4563     {
4564       // there is an adjacent face, check order of nodes in it
4565       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4566       if ( sameOrder )
4567       {
4568         std::swap( itNN[0],    itNN[1] );
4569         std::swap( prevNod[0], prevNod[1] );
4570         std::swap( nextNod[0], nextNod[1] );
4571         std::swap( isSingleNode[0], isSingleNode[1] );
4572         if ( nbSame > 0 )
4573           sames[0] = 1 - sames[0];
4574         iNotSameNode = 1 - iNotSameNode;
4575       }
4576     }
4577   }
4578
4579   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4580   if ( nbSame > 0 ) {
4581     iSameNode    = sames[ nbSame-1 ];
4582     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4583     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4584     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4585   }
4586
4587   if ( baseType == SMDSEntity_Polygon )
4588   {
4589     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4590     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4591   }
4592   else if ( baseType == SMDSEntity_Quad_Polygon )
4593   {
4594     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4595     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4596   }
4597
4598   // make new elements
4599   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4600   {
4601     // get next nodes
4602     for ( iNode = 0; iNode < nbNodes; iNode++ )
4603     {
4604       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4605       nextNod[ iNode ] = *itNN[ iNode ]++;
4606     }
4607
4608     SMDS_MeshElement* aNewElem = 0;
4609     /*if(!elem->IsPoly())*/ {
4610       switch ( baseType ) {
4611       case SMDSEntity_0D:
4612       case SMDSEntity_Node: { // sweep NODE
4613         if ( nbSame == 0 ) {
4614           if ( isSingleNode[0] )
4615             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4616           else
4617             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4618         }
4619         else
4620           return;
4621         break;
4622       }
4623       case SMDSEntity_Edge: { // sweep EDGE
4624         if ( nbDouble == 0 )
4625         {
4626           if ( nbSame == 0 ) // ---> quadrangle
4627             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4628                                       nextNod[ 1 ], nextNod[ 0 ] );
4629           else               // ---> triangle
4630             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4631                                       nextNod[ iNotSameNode ] );
4632         }
4633         else                 // ---> polygon
4634         {
4635           vector<const SMDS_MeshNode*> poly_nodes;
4636           poly_nodes.push_back( prevNod[0] );
4637           poly_nodes.push_back( prevNod[1] );
4638           if ( prevNod[1] != nextNod[1] )
4639           {
4640             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4641             poly_nodes.push_back( nextNod[1] );
4642           }
4643           if ( prevNod[0] != nextNod[0] )
4644           {
4645             poly_nodes.push_back( nextNod[0] );
4646             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4647           }
4648           switch ( poly_nodes.size() ) {
4649           case 3:
4650             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4651             break;
4652           case 4:
4653             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4654                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4655             break;
4656           default:
4657             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4658           }
4659         }
4660         break;
4661       }
4662       case SMDSEntity_Triangle: // TRIANGLE --->
4663         {
4664           if ( nbDouble > 0 ) break;
4665           if ( nbSame == 0 )       // ---> pentahedron
4666             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4667                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4668
4669           else if ( nbSame == 1 )  // ---> pyramid
4670             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4671                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4672                                          nextNod[ iSameNode ]);
4673
4674           else // 2 same nodes:       ---> tetrahedron
4675             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4676                                          nextNod[ iNotSameNode ]);
4677           break;
4678         }
4679       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4680         {
4681           if ( nbSame == 2 )
4682             return;
4683           if ( nbDouble+nbSame == 2 )
4684           {
4685             if(nbSame==0) {      // ---> quadratic quadrangle
4686               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4687                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4688             }
4689             else { //(nbSame==1) // ---> quadratic triangle
4690               if(sames[0]==2) {
4691                 return; // medium node on axis
4692               }
4693               else if(sames[0]==0)
4694                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4695                                           prevNod[2], midlNod[1], nextNod[2] );
4696               else // sames[0]==1
4697                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4698                                           prevNod[2], nextNod[2], midlNod[0]);
4699             }
4700           }
4701           else if ( nbDouble == 3 )
4702           {
4703             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4704               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4705                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4706             }
4707           }
4708           else
4709             return;
4710           break;
4711         }
4712       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4713         if ( nbDouble > 0 ) break;
4714
4715         if ( nbSame == 0 )       // ---> hexahedron
4716           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4717                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4718
4719         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4720           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4721                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4722                                        nextNod[ iSameNode ]);
4723           newElems.push_back( aNewElem );
4724           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4725                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4726                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4727         }
4728         else if ( nbSame == 2 ) { // ---> pentahedron
4729           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4730             // iBeforeSame is same too
4731             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4732                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4733                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4734           else
4735             // iAfterSame is same too
4736             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4737                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4738                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4739         }
4740         break;
4741       }
4742       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4743       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4744         if ( nbDouble+nbSame != 3 ) break;
4745         if(nbSame==0) {
4746           // --->  pentahedron with 15 nodes
4747           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4748                                        nextNod[0], nextNod[1], nextNod[2],
4749                                        prevNod[3], prevNod[4], prevNod[5],
4750                                        nextNod[3], nextNod[4], nextNod[5],
4751                                        midlNod[0], midlNod[1], midlNod[2]);
4752         }
4753         else if(nbSame==1) {
4754           // --->  2d order pyramid of 13 nodes
4755           int apex = iSameNode;
4756           int i0 = ( apex + 1 ) % nbCorners;
4757           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4758           int i0a = apex + 3;
4759           int i1a = i1 + 3;
4760           int i01 = i0 + 3;
4761           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4762                                       nextNod[i0], nextNod[i1], prevNod[apex],
4763                                       prevNod[i01], midlNod[i0],
4764                                       nextNod[i01], midlNod[i1],
4765                                       prevNod[i1a], prevNod[i0a],
4766                                       nextNod[i0a], nextNod[i1a]);
4767         }
4768         else if(nbSame==2) {
4769           // --->  2d order tetrahedron of 10 nodes
4770           int n1 = iNotSameNode;
4771           int n2 = ( n1 + 1             ) % nbCorners;
4772           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4773           int n12 = n1 + 3;
4774           int n23 = n2 + 3;
4775           int n31 = n3 + 3;
4776           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4777                                        prevNod[n12], prevNod[n23], prevNod[n31],
4778                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4779         }
4780         break;
4781       }
4782       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4783         if( nbSame == 0 ) {
4784           if ( nbDouble != 4 ) break;
4785           // --->  hexahedron with 20 nodes
4786           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4787                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4788                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4789                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4790                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4791         }
4792         else if(nbSame==1) {
4793           // ---> pyramid + pentahedron - can not be created since it is needed
4794           // additional middle node at the center of face
4795           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4796           return;
4797         }
4798         else if( nbSame == 2 ) {
4799           if ( nbDouble != 2 ) break;
4800           // --->  2d order Pentahedron with 15 nodes
4801           int n1,n2,n4,n5;
4802           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4803             // iBeforeSame is same too
4804             n1 = iBeforeSame;
4805             n2 = iOpposSame;
4806             n4 = iSameNode;
4807             n5 = iAfterSame;
4808           }
4809           else {
4810             // iAfterSame is same too
4811             n1 = iSameNode;
4812             n2 = iBeforeSame;
4813             n4 = iAfterSame;
4814             n5 = iOpposSame;
4815           }
4816           int n12 = n2 + 4;
4817           int n45 = n4 + 4;
4818           int n14 = n1 + 4;
4819           int n25 = n5 + 4;
4820           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4821                                        prevNod[n4], prevNod[n5], nextNod[n5],
4822                                        prevNod[n12], midlNod[n2], nextNod[n12],
4823                                        prevNod[n45], midlNod[n5], nextNod[n45],
4824                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4825         }
4826         break;
4827       }
4828       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4829
4830         if( nbSame == 0 && nbDouble == 9 ) {
4831           // --->  tri-quadratic hexahedron with 27 nodes
4832           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4833                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4834                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4835                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4836                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4837                                        prevNod[8], // bottom center
4838                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4839                                        nextNod[8], // top center
4840                                        midlNod[8]);// elem center
4841         }
4842         else
4843         {
4844           return;
4845         }
4846         break;
4847       }
4848       case SMDSEntity_Polygon: { // sweep POLYGON
4849
4850         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4851           // --->  hexagonal prism
4852           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4853                                        prevNod[3], prevNod[4], prevNod[5],
4854                                        nextNod[0], nextNod[1], nextNod[2],
4855                                        nextNod[3], nextNod[4], nextNod[5]);
4856         }
4857         break;
4858       }
4859       case SMDSEntity_Ball:
4860         return;
4861
4862       default:
4863         break;
4864       } // switch ( baseType )
4865     } // scope
4866
4867     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4868     {
4869       if ( baseType != SMDSEntity_Polygon )
4870       {
4871         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4872         SMDS_MeshCell::applyInterlace( ind, prevNod );
4873         SMDS_MeshCell::applyInterlace( ind, nextNod );
4874         SMDS_MeshCell::applyInterlace( ind, midlNod );
4875         SMDS_MeshCell::applyInterlace( ind, itNN );
4876         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4877         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4878       }
4879       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4880       vector<int> quantities (nbNodes + 2);
4881       polyedre_nodes.clear();
4882       quantities.clear();
4883
4884       // bottom of prism
4885       for (int inode = 0; inode < nbNodes; inode++)
4886         polyedre_nodes.push_back( prevNod[inode] );
4887       quantities.push_back( nbNodes );
4888
4889       // top of prism
4890       polyedre_nodes.push_back( nextNod[0] );
4891       for (int inode = nbNodes; inode-1; --inode )
4892         polyedre_nodes.push_back( nextNod[inode-1] );
4893       quantities.push_back( nbNodes );
4894
4895       // side faces
4896       // 3--6--2
4897       // |     |
4898       // 7     5
4899       // |     |
4900       // 0--4--1
4901       const int iQuad = elem->IsQuadratic();
4902       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4903       {
4904         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4905         int inextface = (iface+1+iQuad) % nbNodes;
4906         int imid      = (iface+1) % nbNodes;
4907         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4908         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4909         polyedre_nodes.push_back( prevNod[iface] );             // 1
4910         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4911         {
4912           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4913           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4914         }
4915         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4916         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4917         {
4918           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4919           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4920         }
4921         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4922         if ( nbFaceNodes > 2 )
4923           quantities.push_back( nbFaceNodes );
4924         else // degenerated face
4925           polyedre_nodes.resize( prevNbNodes );
4926       }
4927       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4928
4929     } // try to create a polyherdal prism
4930
4931     if ( aNewElem ) {
4932       newElems.push_back( aNewElem );
4933       myLastCreatedElems.Append(aNewElem);
4934       srcElements.Append( elem );
4935     }
4936
4937     // set new prev nodes
4938     for ( iNode = 0; iNode < nbNodes; iNode++ )
4939       prevNod[ iNode ] = nextNod[ iNode ];
4940
4941   } // loop on steps
4942 }
4943
4944 //=======================================================================
4945 /*!
4946  * \brief Create 1D and 2D elements around swept elements
4947  * \param mapNewNodes - source nodes and ones generated from them
4948  * \param newElemsMap - source elements and ones generated from them
4949  * \param elemNewNodesMap - nodes generated from each node of each element
4950  * \param elemSet - all swept elements
4951  * \param nbSteps - number of sweeping steps
4952  * \param srcElements - to append elem for each generated element
4953  */
4954 //=======================================================================
4955
4956 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4957                                   TTElemOfElemListMap &    newElemsMap,
4958                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4959                                   TIDSortedElemSet&        elemSet,
4960                                   const int                nbSteps,
4961                                   SMESH_SequenceOfElemPtr& srcElements)
4962 {
4963   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4964   SMESHDS_Mesh* aMesh = GetMeshDS();
4965
4966   // Find nodes belonging to only one initial element - sweep them into edges.
4967
4968   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4969   for ( ; nList != mapNewNodes.end(); nList++ )
4970   {
4971     const SMDS_MeshNode* node =
4972       static_cast<const SMDS_MeshNode*>( nList->first );
4973     if ( newElemsMap.count( node ))
4974       continue; // node was extruded into edge
4975     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4976     int nbInitElems = 0;
4977     const SMDS_MeshElement* el = 0;
4978     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4979     while ( eIt->more() && nbInitElems < 2 ) {
4980       const SMDS_MeshElement* e = eIt->next();
4981       SMDSAbs_ElementType  type = e->GetType();
4982       if ( type == SMDSAbs_Volume ||
4983            type < highType ||
4984            !elemSet.count(e))
4985         continue;
4986       if ( type > highType ) {
4987         nbInitElems = 0;
4988         highType    = type;
4989       }
4990       el = e;
4991       ++nbInitElems;
4992     }
4993     if ( nbInitElems == 1 ) {
4994       bool NotCreateEdge = el && el->IsMediumNode(node);
4995       if(!NotCreateEdge) {
4996         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4997         list<const SMDS_MeshElement*> newEdges;
4998         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4999       }
5000     }
5001   }
5002
5003   // Make a ceiling for each element ie an equal element of last new nodes.
5004   // Find free links of faces - make edges and sweep them into faces.
5005
5006   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5007
5008   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5009   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5010   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5011   {
5012     const SMDS_MeshElement* elem = itElem->first;
5013     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5014
5015     if(itElem->second.size()==0) continue;
5016
5017     const bool isQuadratic = elem->IsQuadratic();
5018
5019     if ( elem->GetType() == SMDSAbs_Edge ) {
5020       // create a ceiling edge
5021       if ( !isQuadratic ) {
5022         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5023                                vecNewNodes[ 1 ]->second.back())) {
5024           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5025                                                    vecNewNodes[ 1 ]->second.back()));
5026           srcElements.Append( elem );
5027         }
5028       }
5029       else {
5030         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5031                                vecNewNodes[ 1 ]->second.back(),
5032                                vecNewNodes[ 2 ]->second.back())) {
5033           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5034                                                    vecNewNodes[ 1 ]->second.back(),
5035                                                    vecNewNodes[ 2 ]->second.back()));
5036           srcElements.Append( elem );
5037         }
5038       }
5039     }
5040     if ( elem->GetType() != SMDSAbs_Face )
5041       continue;
5042
5043     bool hasFreeLinks = false;
5044
5045     TIDSortedElemSet avoidSet;
5046     avoidSet.insert( elem );
5047
5048     set<const SMDS_MeshNode*> aFaceLastNodes;
5049     int iNode, nbNodes = vecNewNodes.size();
5050     if ( !isQuadratic ) {
5051       // loop on the face nodes
5052       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5053         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5054         // look for free links of the face
5055         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5056         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5057         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5058         // check if a link n1-n2 is free
5059         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5060           hasFreeLinks = true;
5061           // make a new edge and a ceiling for a new edge
5062           const SMDS_MeshElement* edge;
5063           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5064             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5065             srcElements.Append( myLastCreatedElems.Last() );
5066           }
5067           n1 = vecNewNodes[ iNode ]->second.back();
5068           n2 = vecNewNodes[ iNext ]->second.back();
5069           if ( !aMesh->FindEdge( n1, n2 )) {
5070             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5071             srcElements.Append( edge );
5072           }
5073         }
5074       }
5075     }
5076     else { // elem is quadratic face
5077       int nbn = nbNodes/2;
5078       for ( iNode = 0; iNode < nbn; iNode++ ) {
5079         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5080         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5081         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5082         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5083         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5084         // check if a link is free
5085         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5086              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5087              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5088           hasFreeLinks = true;
5089           // make an edge and a ceiling for a new edge
5090           // find medium node
5091           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5092             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5093             srcElements.Append( elem );
5094           }
5095           n1 = vecNewNodes[ iNode ]->second.back();
5096           n2 = vecNewNodes[ iNext ]->second.back();
5097           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5098           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5099             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5100             srcElements.Append( elem );
5101           }
5102         }
5103       }
5104       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5105         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5106       }
5107     }
5108
5109     // sweep free links into faces
5110
5111     if ( hasFreeLinks ) {
5112       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5113       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5114
5115       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5116       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5117       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5118         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5119         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5120       }
5121       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5122         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5123         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5124       }
5125       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5126         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5127         std::advance( v, volNb );
5128         // find indices of free faces of a volume and their source edges
5129         list< int > freeInd;
5130         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5131         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5132         int iF, nbF = vTool.NbFaces();
5133         for ( iF = 0; iF < nbF; iF ++ ) {
5134           if (vTool.IsFreeFace( iF ) &&
5135               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5136               initNodeSet != faceNodeSet) // except an initial face
5137           {
5138             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5139               continue;
5140             if ( faceNodeSet == initNodeSetNoCenter )
5141               continue;
5142             freeInd.push_back( iF );
5143             // find source edge of a free face iF
5144             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5145             vector<const SMDS_MeshNode*>::iterator lastCommom;
5146             commonNodes.resize( nbNodes, 0 );
5147             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5148                                                 initNodeSet.begin(), initNodeSet.end(),
5149                                                 commonNodes.begin());
5150             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5151               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5152             else
5153               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5154 #ifdef _DEBUG_
5155             if ( !srcEdges.back() )
5156             {
5157               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5158                    << iF << " of volume #" << vTool.ID() << endl;
5159             }
5160 #endif
5161           }
5162         }
5163         if ( freeInd.empty() )
5164           continue;
5165
5166         // create wall faces for all steps;
5167         // if such a face has been already created by sweep of edge,
5168         // assure that its orientation is OK
5169         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5170         {
5171           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5172           vTool.SetExternalNormal();
5173           const int nextShift = vTool.IsForward() ? +1 : -1;
5174           list< int >::iterator ind = freeInd.begin();
5175           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5176           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5177           {
5178             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5179             int nbn = vTool.NbFaceNodes( *ind );
5180             const SMDS_MeshElement * f = 0;
5181             if ( nbn == 3 )              ///// triangle
5182             {
5183               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5184               if ( !f ||
5185                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5186               {
5187                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5188                                                      nodes[ 1 ],
5189                                                      nodes[ 1 + nextShift ] };
5190                 if ( f )
5191                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5192                 else
5193                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5194                                                             newOrder[ 2 ] ));
5195               }
5196             }
5197             else if ( nbn == 4 )       ///// quadrangle
5198             {
5199               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5200               if ( !f ||
5201                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5202               {
5203                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5204                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5205                 if ( f )
5206                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5207                 else
5208                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5209                                                             newOrder[ 2 ], newOrder[ 3 ]));
5210               }
5211             }
5212             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5213             {
5214               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5215               if ( !f ||
5216                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5217               {
5218                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5219                                                      nodes[2],
5220                                                      nodes[2 + 2*nextShift],
5221                                                      nodes[3 - 2*nextShift],
5222                                                      nodes[3],
5223                                                      nodes[3 + 2*nextShift]};
5224                 if ( f )
5225                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5226                 else
5227                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5228                                                             newOrder[ 1 ],
5229                                                             newOrder[ 2 ],
5230                                                             newOrder[ 3 ],
5231                                                             newOrder[ 4 ],
5232                                                             newOrder[ 5 ] ));
5233               }
5234             }
5235             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5236             {
5237               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5238                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5239               if ( !f ||
5240                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5241               {
5242                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5243                                                      nodes[4 - 2*nextShift],
5244                                                      nodes[4],
5245                                                      nodes[4 + 2*nextShift],
5246                                                      nodes[1],
5247                                                      nodes[5 - 2*nextShift],
5248                                                      nodes[5],
5249                                                      nodes[5 + 2*nextShift] };
5250                 if ( f )
5251                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5252                 else
5253                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5254                                                            newOrder[ 2 ], newOrder[ 3 ],
5255                                                            newOrder[ 4 ], newOrder[ 5 ],
5256                                                            newOrder[ 6 ], newOrder[ 7 ]));
5257               }
5258             }
5259             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5260             {
5261               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5262                                       SMDSAbs_Face, /*noMedium=*/false);
5263               if ( !f ||
5264                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5265               {
5266                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5267                                                      nodes[4 - 2*nextShift],
5268                                                      nodes[4],
5269                                                      nodes[4 + 2*nextShift],
5270                                                      nodes[1],
5271                                                      nodes[5 - 2*nextShift],
5272                                                      nodes[5],
5273                                                      nodes[5 + 2*nextShift],
5274                                                      nodes[8] };
5275                 if ( f )
5276                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5277                 else
5278                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5279                                                            newOrder[ 2 ], newOrder[ 3 ],
5280                                                            newOrder[ 4 ], newOrder[ 5 ],
5281                                                            newOrder[ 6 ], newOrder[ 7 ],
5282                                                            newOrder[ 8 ]));
5283               }
5284             }
5285             else  //////// polygon
5286             {
5287               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5288               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5289               if ( !f ||
5290                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5291               {
5292                 if ( !vTool.IsForward() )
5293                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5294                 if ( f )
5295                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5296                 else
5297                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5298               }
5299             }
5300
5301             while ( srcElements.Length() < myLastCreatedElems.Length() )
5302               srcElements.Append( *srcEdge );
5303
5304           }  // loop on free faces
5305
5306           // go to the next volume
5307           iVol = 0;
5308           while ( iVol++ < nbVolumesByStep ) v++;
5309
5310         } // loop on steps
5311       } // loop on volumes of one step
5312     } // sweep free links into faces
5313
5314     // Make a ceiling face with a normal external to a volume
5315
5316     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5317     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5318     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5319
5320     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5321       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5322       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5323     }
5324     if ( iF >= 0 )
5325     {
5326       lastVol.SetExternalNormal();
5327       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5328       const               int nbn = lastVol.NbFaceNodes( iF );
5329       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5330       if ( !hasFreeLinks ||
5331            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5332       {
5333         const vector<int>& interlace =
5334           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5335         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5336
5337         AddElement( nodeVec, anyFace.Init( elem ));
5338
5339         while ( srcElements.Length() < myLastCreatedElems.Length() )
5340           srcElements.Append( elem );
5341       }
5342     }
5343   } // loop on swept elements
5344 }
5345
5346 //=======================================================================
5347 //function : RotationSweep
5348 //purpose  :
5349 //=======================================================================
5350
5351 SMESH_MeshEditor::PGroupIDs
5352 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5353                                 const gp_Ax1&      theAxis,
5354                                 const double       theAngle,
5355                                 const int          theNbSteps,
5356                                 const double       theTol,
5357                                 const bool         theMakeGroups,
5358                                 const bool         theMakeWalls)
5359 {
5360   myLastCreatedElems.Clear();
5361   myLastCreatedNodes.Clear();
5362
5363   // source elements for each generated one
5364   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5365
5366   gp_Trsf aTrsf;
5367   aTrsf.SetRotation( theAxis, theAngle );
5368   gp_Trsf aTrsf2;
5369   aTrsf2.SetRotation( theAxis, theAngle/2. );
5370
5371   gp_Lin aLine( theAxis );
5372   double aSqTol = theTol * theTol;
5373
5374   SMESHDS_Mesh* aMesh = GetMeshDS();
5375
5376   TNodeOfNodeListMap mapNewNodes;
5377   TElemOfVecOfNnlmiMap mapElemNewNodes;
5378   TTElemOfElemListMap newElemsMap;
5379
5380   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5381                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5382                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5383   // loop on theElemSets
5384   setElemsFirst( theElemSets );
5385   TIDSortedElemSet::iterator itElem;
5386   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5387   {
5388     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5389     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5390       const SMDS_MeshElement* elem = *itElem;
5391       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5392         continue;
5393       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5394       newNodesItVec.reserve( elem->NbNodes() );
5395
5396       // loop on elem nodes
5397       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5398       while ( itN->more() )
5399       {
5400         const SMDS_MeshNode* node = cast2Node( itN->next() );
5401
5402         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5403         double coord[3];
5404         aXYZ.Coord( coord[0], coord[1], coord[2] );
5405         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5406
5407         // check if a node has been already sweeped
5408         TNodeOfNodeListMapItr nIt =
5409           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5410         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5411         if ( listNewNodes.empty() )
5412         {
5413           // check if we are to create medium nodes between corner ones
5414           bool needMediumNodes = false;
5415           if ( isQuadraticMesh )
5416           {
5417             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5418             while (it->more() && !needMediumNodes )
5419             {
5420               const SMDS_MeshElement* invElem = it->next();
5421               if ( invElem != elem && !theElems.count( invElem )) continue;
5422               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5423               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5424                 needMediumNodes = true;
5425             }
5426           }
5427
5428           // make new nodes
5429           const SMDS_MeshNode * newNode = node;
5430           for ( int i = 0; i < theNbSteps; i++ ) {
5431             if ( !isOnAxis ) {
5432               if ( needMediumNodes )  // create a medium node
5433               {
5434                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5435                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5436                 myLastCreatedNodes.Append(newNode);
5437                 srcNodes.Append( node );
5438                 listNewNodes.push_back( newNode );
5439                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5440               }
5441               else {
5442                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5443               }
5444               // create a corner node
5445               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5446               myLastCreatedNodes.Append(newNode);
5447               srcNodes.Append( node );
5448               listNewNodes.push_back( newNode );
5449             }
5450             else {
5451               listNewNodes.push_back( newNode );
5452               // if ( needMediumNodes )
5453               //   listNewNodes.push_back( newNode );
5454             }
5455           }
5456         }
5457         newNodesItVec.push_back( nIt );
5458       }
5459       // make new elements
5460       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5461     }
5462   }
5463
5464   if ( theMakeWalls )
5465     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5466
5467   PGroupIDs newGroupIDs;
5468   if ( theMakeGroups )
5469     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5470
5471   return newGroupIDs;
5472 }
5473
5474 //=======================================================================
5475 //function : ExtrusParam
5476 //purpose  : standard construction
5477 //=======================================================================
5478
5479 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5480                                             const int                theNbSteps,
5481                                             const std::list<double>& theScales,
5482                                             const gp_XYZ*            theBasePoint,
5483                                             const int                theFlags,
5484                                             const double             theTolerance):
5485   myDir( theStep ),
5486   myBaseP( Precision::Infinite(), 0, 0 ),
5487   myFlags( theFlags ),
5488   myTolerance( theTolerance ),
5489   myElemsToUse( NULL )
5490 {
5491   mySteps = new TColStd_HSequenceOfReal;
5492   const double stepSize = theStep.Magnitude();
5493   for (int i=1; i<=theNbSteps; i++ )
5494     mySteps->Append( stepSize );
5495
5496   int nbScales = theScales.size();
5497   if ( nbScales > 0 )
5498   {
5499     if ( IsLinearVariation() && nbScales < theNbSteps )
5500     {
5501       myScales.reserve( theNbSteps );
5502       std::list<double>::const_iterator scale = theScales.begin();
5503       double prevScale = 1.0;
5504       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5505       {
5506         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5507         int    stDelta = Max( 1, iStep - myScales.size());
5508         double scDelta = ( *scale - prevScale ) / stDelta;
5509         for ( int iStep = 0; iStep < stDelta; ++iStep )
5510         {
5511           myScales.push_back( prevScale + scDelta );
5512           prevScale = myScales.back();
5513         }
5514         prevScale = *scale;
5515       }
5516     }
5517     else
5518     {
5519       myScales.assign( theScales.begin(), theScales.end() );
5520     }
5521   }
5522   if ( theBasePoint )
5523   {
5524     myBaseP = *theBasePoint;
5525   }
5526
5527   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5528       ( theTolerance > 0 ))
5529   {
5530     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5531   }
5532   else
5533   {
5534     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5535   }
5536 }
5537
5538 //=======================================================================
5539 //function : ExtrusParam
5540 //purpose  : steps are given explicitly
5541 //=======================================================================
5542
5543 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5544                                             Handle(TColStd_HSequenceOfReal) theSteps,
5545                                             const int                       theFlags,
5546                                             const double                    theTolerance):
5547   myDir( theDir ),
5548   mySteps( theSteps ),
5549   myFlags( theFlags ),
5550   myTolerance( theTolerance ),
5551   myElemsToUse( NULL )
5552 {
5553   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5554       ( theTolerance > 0 ))
5555   {
5556     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5557   }
5558   else
5559   {
5560     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5561   }
5562 }
5563
5564 //=======================================================================
5565 //function : ExtrusParam
5566 //purpose  : for extrusion by normal
5567 //=======================================================================
5568
5569 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5570                                             const int    theNbSteps,
5571                                             const int    theFlags,
5572                                             const int    theDim ):
5573   myDir( 1,0,0 ),
5574   mySteps( new TColStd_HSequenceOfReal ),
5575   myFlags( theFlags ),
5576   myTolerance( 0 ),
5577   myElemsToUse( NULL )
5578 {
5579   for (int i = 0; i < theNbSteps; i++ )
5580     mySteps->Append( theStepSize );
5581
5582   if ( theDim == 1 )
5583   {
5584     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5585   }
5586   else
5587   {
5588     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5589   }
5590 }
5591
5592 //=======================================================================
5593 //function : ExtrusParam::SetElementsToUse
5594 //purpose  : stores elements to use for extrusion by normal, depending on
5595 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5596 //           define myBaseP for scaling
5597 //=======================================================================
5598
5599 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5600                                                       const TIDSortedElemSet& nodes )
5601 {
5602   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5603
5604   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5605   {
5606     myBaseP.SetCoord( 0.,0.,0. );
5607     TIDSortedElemSet newNodes;
5608
5609     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5610     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5611     {
5612       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5613       TIDSortedElemSet::const_iterator itElem = elements.begin();
5614       for ( ; itElem != elements.end(); itElem++ )
5615       {
5616         const SMDS_MeshElement* elem = *itElem;
5617         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5618         while ( itN->more() ) {
5619           const SMDS_MeshElement* node = itN->next();
5620           if ( newNodes.insert( node ).second )
5621             myBaseP += SMESH_TNodeXYZ( node );
5622         }
5623       }
5624     }
5625     myBaseP /= newNodes.size();
5626   }
5627 }
5628
5629 //=======================================================================
5630 //function : ExtrusParam::beginStepIter
5631 //purpose  : prepare iteration on steps
5632 //=======================================================================
5633
5634 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5635 {
5636   myWithMediumNodes = withMediumNodes;
5637   myNextStep = 1;
5638   myCurSteps.clear();
5639 }
5640 //=======================================================================
5641 //function : ExtrusParam::moreSteps
5642 //purpose  : are there more steps?
5643 //=======================================================================
5644
5645 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5646 {
5647   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5648 }
5649 //=======================================================================
5650 //function : ExtrusParam::nextStep
5651 //purpose  : returns the next step
5652 //=======================================================================
5653
5654 double SMESH_MeshEditor::ExtrusParam::nextStep()
5655 {
5656   double res = 0;
5657   if ( !myCurSteps.empty() )
5658   {
5659     res = myCurSteps.back();
5660     myCurSteps.pop_back();
5661   }
5662   else if ( myNextStep <= mySteps->Length() )
5663   {
5664     myCurSteps.push_back( mySteps->Value( myNextStep ));
5665     ++myNextStep;
5666     if ( myWithMediumNodes )
5667     {
5668       myCurSteps.back() /= 2.;
5669       myCurSteps.push_back( myCurSteps.back() );
5670     }
5671     res = nextStep();
5672   }
5673   return res;
5674 }
5675
5676 //=======================================================================
5677 //function : ExtrusParam::makeNodesByDir
5678 //purpose  : create nodes for standard extrusion
5679 //=======================================================================
5680
5681 int SMESH_MeshEditor::ExtrusParam::
5682 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5683                 const SMDS_MeshNode*              srcNode,
5684                 std::list<const SMDS_MeshNode*> & newNodes,
5685                 const bool                        makeMediumNodes)
5686 {
5687   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5688
5689   int nbNodes = 0;
5690   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5691   {
5692     p += myDir.XYZ() * nextStep();
5693     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5694     newNodes.push_back( newNode );
5695   }
5696
5697   if ( !myScales.empty() )
5698   {
5699     if ( makeMediumNodes && myMediumScales.empty() )
5700     {
5701       myMediumScales.resize( myScales.size() );
5702       double prevFactor = 1.;
5703       for ( size_t i = 0; i < myScales.size(); ++i )
5704       {
5705         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5706         prevFactor = myScales[i];
5707       }
5708     }
5709     typedef std::vector<double>::iterator ScaleIt;
5710     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5711
5712     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5713
5714     gp_XYZ center = myBaseP;
5715     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5716     size_t iN  = 0;
5717     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5718     {
5719       center += myDir.XYZ() * nextStep();
5720
5721       iSc += int( makeMediumNodes );
5722       ScaleIt& scale = scales[ iSc % 2 ];
5723       
5724       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5725       xyz = ( *scale * ( xyz - center )) + center;
5726       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5727
5728       ++scale;
5729     }
5730   }
5731   return nbNodes;
5732 }
5733
5734 //=======================================================================
5735 //function : ExtrusParam::makeNodesByDirAndSew
5736 //purpose  : create nodes for standard extrusion with sewing
5737 //=======================================================================
5738
5739 int SMESH_MeshEditor::ExtrusParam::
5740 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5741                       const SMDS_MeshNode*              srcNode,
5742                       std::list<const SMDS_MeshNode*> & newNodes,
5743                       const bool                        makeMediumNodes)
5744 {
5745   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5746
5747   int nbNodes = 0;
5748   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5749   {
5750     P1 += myDir.XYZ() * nextStep();
5751
5752     // try to search in sequence of existing nodes
5753     // if myNodes.Length()>0 we 'nave to use given sequence
5754     // else - use all nodes of mesh
5755     const SMDS_MeshNode * node = 0;
5756     if ( myNodes.Length() > 0 ) {
5757       int i;
5758       for(i=1; i<=myNodes.Length(); i++) {
5759         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5760         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5761         {
5762           node = myNodes.Value(i);
5763           break;
5764         }
5765       }
5766     }
5767     else {
5768       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5769       while(itn->more()) {
5770         SMESH_TNodeXYZ P2( itn->next() );
5771         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5772         {
5773           node = P2._node;
5774           break;
5775         }
5776       }
5777     }
5778
5779     if ( !node )
5780       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5781
5782     newNodes.push_back( node );
5783
5784   } // loop on steps
5785
5786   return nbNodes;
5787 }
5788
5789 //=======================================================================
5790 //function : ExtrusParam::makeNodesByNormal2D
5791 //purpose  : create nodes for extrusion using normals of faces
5792 //=======================================================================
5793
5794 int SMESH_MeshEditor::ExtrusParam::
5795 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5796                      const SMDS_MeshNode*              srcNode,
5797                      std::list<const SMDS_MeshNode*> & newNodes,
5798                      const bool                        makeMediumNodes)
5799 {
5800   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5801
5802   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5803
5804   // get normals to faces sharing srcNode
5805   vector< gp_XYZ > norms, baryCenters;
5806   gp_XYZ norm, avgNorm( 0,0,0 );
5807   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5808   while ( faceIt->more() )
5809   {
5810     const SMDS_MeshElement* face = faceIt->next();
5811     if ( myElemsToUse && !myElemsToUse->count( face ))
5812       continue;
5813     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5814     {
5815       norms.push_back( norm );
5816       avgNorm += norm;
5817       if ( !alongAvgNorm )
5818       {
5819         gp_XYZ bc(0,0,0);
5820         int nbN = 0;
5821         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5822           bc += SMESH_TNodeXYZ( nIt->next() );
5823         baryCenters.push_back( bc / nbN );
5824       }
5825     }
5826   }
5827
5828   if ( norms.empty() ) return 0;
5829
5830   double normSize = avgNorm.Modulus();
5831   if ( normSize < std::numeric_limits<double>::min() )
5832     return 0;
5833
5834   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5835   {
5836     myDir = avgNorm;
5837     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5838   }
5839
5840   avgNorm /= normSize;
5841
5842   int nbNodes = 0;
5843   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5844   {
5845     gp_XYZ pNew = p;
5846     double stepSize = nextStep();
5847
5848     if ( norms.size() > 1 )
5849     {
5850       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5851       {
5852         // translate plane of a face
5853         baryCenters[ iF ] += norms[ iF ] * stepSize;
5854
5855         // find point of intersection of the face plane located at baryCenters[ iF ]
5856         // and avgNorm located at pNew
5857         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5858         double dot  = ( norms[ iF ] * avgNorm );
5859         if ( dot < std::numeric_limits<double>::min() )
5860           dot = stepSize * 1e-3;
5861         double step = -( norms[ iF ] * pNew + d ) / dot;
5862         pNew += step * avgNorm;
5863       }
5864     }
5865     else
5866     {
5867       pNew += stepSize * avgNorm;
5868     }
5869     p = pNew;
5870
5871     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5872     newNodes.push_back( newNode );
5873   }
5874   return nbNodes;
5875 }
5876
5877 //=======================================================================
5878 //function : ExtrusParam::makeNodesByNormal1D
5879 //purpose  : create nodes for extrusion using normals of edges
5880 //=======================================================================
5881
5882 int SMESH_MeshEditor::ExtrusParam::
5883 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5884                      const SMDS_MeshNode*              srcNode,
5885                      std::list<const SMDS_MeshNode*> & newNodes,
5886                      const bool                        makeMediumNodes)
5887 {
5888   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5889   return 0;
5890 }
5891
5892 //=======================================================================
5893 //function : ExtrusionSweep
5894 //purpose  :
5895 //=======================================================================
5896
5897 SMESH_MeshEditor::PGroupIDs
5898 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5899                                   const gp_Vec&        theStep,
5900                                   const int            theNbSteps,
5901                                   TTElemOfElemListMap& newElemsMap,
5902                                   const int            theFlags,
5903                                   const double         theTolerance)
5904 {
5905   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5906   return ExtrusionSweep( theElems, aParams, newElemsMap );
5907 }
5908
5909
5910 //=======================================================================
5911 //function : ExtrusionSweep
5912 //purpose  :
5913 //=======================================================================
5914
5915 SMESH_MeshEditor::PGroupIDs
5916 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5917                                   ExtrusParam&         theParams,
5918                                   TTElemOfElemListMap& newElemsMap)
5919 {
5920   myLastCreatedElems.Clear();
5921   myLastCreatedNodes.Clear();
5922
5923   // source elements for each generated one
5924   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5925
5926   setElemsFirst( theElemSets );
5927   const int nbSteps = theParams.NbSteps();
5928   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5929
5930   TNodeOfNodeListMap   mapNewNodes;
5931   TElemOfVecOfNnlmiMap mapElemNewNodes;
5932
5933   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5934                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5935                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5936   // loop on theElems
5937   TIDSortedElemSet::iterator itElem;
5938   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5939   {
5940     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5941     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5942     {
5943       // check element type
5944       const SMDS_MeshElement* elem = *itElem;
5945       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5946         continue;
5947
5948       const size_t nbNodes = elem->NbNodes();
5949       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5950       newNodesItVec.reserve( nbNodes );
5951
5952       // loop on elem nodes
5953       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5954       while ( itN->more() )
5955       {
5956         // check if a node has been already sweeped
5957         const SMDS_MeshNode* node = cast2Node( itN->next() );
5958         TNodeOfNodeListMap::iterator nIt =
5959           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5960         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5961         if ( listNewNodes.empty() )
5962         {
5963           // make new nodes
5964
5965           // check if we are to create medium nodes between corner ones
5966           bool needMediumNodes = false;
5967           if ( isQuadraticMesh )
5968           {
5969             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5970             while (it->more() && !needMediumNodes )
5971             {
5972               const SMDS_MeshElement* invElem = it->next();
5973               if ( invElem != elem && !theElems.count( invElem )) continue;
5974               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5975               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5976                 needMediumNodes = true;
5977             }
5978           }
5979           // create nodes for all steps
5980           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5981           {
5982             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5983             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5984             {
5985               myLastCreatedNodes.Append( *newNodesIt );
5986               srcNodes.Append( node );
5987             }
5988           }
5989           else
5990           {
5991             break; // newNodesItVec will be shorter than nbNodes
5992           }
5993         }
5994         newNodesItVec.push_back( nIt );
5995       }
5996       // make new elements
5997       if ( newNodesItVec.size() == nbNodes )
5998         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5999     }
6000   }
6001
6002   if ( theParams.ToMakeBoundary() ) {
6003     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6004   }
6005   PGroupIDs newGroupIDs;
6006   if ( theParams.ToMakeGroups() )
6007     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6008
6009   return newGroupIDs;
6010 }
6011
6012 //=======================================================================
6013 //function : ExtrusionAlongTrack
6014 //purpose  :
6015 //=======================================================================
6016 SMESH_MeshEditor::Extrusion_Error
6017 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6018                                        SMESH_subMesh*       theTrack,
6019                                        const SMDS_MeshNode* theN1,
6020                                        const bool           theHasAngles,
6021                                        list<double>&        theAngles,
6022                                        const bool           theLinearVariation,
6023                                        const bool           theHasRefPoint,
6024                                        const gp_Pnt&        theRefPoint,
6025                                        const bool           theMakeGroups)
6026 {
6027   myLastCreatedElems.Clear();
6028   myLastCreatedNodes.Clear();
6029
6030   int aNbE;
6031   std::list<double> aPrms;
6032   TIDSortedElemSet::iterator itElem;
6033
6034   gp_XYZ aGC;
6035   TopoDS_Edge aTrackEdge;
6036   TopoDS_Vertex aV1, aV2;
6037
6038   SMDS_ElemIteratorPtr aItE;
6039   SMDS_NodeIteratorPtr aItN;
6040   SMDSAbs_ElementType aTypeE;
6041
6042   TNodeOfNodeListMap mapNewNodes;
6043
6044   // 1. Check data
6045   aNbE = theElements[0].size() + theElements[1].size();
6046   // nothing to do
6047   if ( !aNbE )
6048     return EXTR_NO_ELEMENTS;
6049
6050   // 1.1 Track Pattern
6051   ASSERT( theTrack );
6052
6053   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6054   if ( !pSubMeshDS )
6055     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6056                                 theHasAngles, theAngles, theLinearVariation,
6057                                 theHasRefPoint, theRefPoint, theMakeGroups );
6058
6059   aItE = pSubMeshDS->GetElements();
6060   while ( aItE->more() ) {
6061     const SMDS_MeshElement* pE = aItE->next();
6062     aTypeE = pE->GetType();
6063     // Pattern must contain links only
6064     if ( aTypeE != SMDSAbs_Edge )
6065       return EXTR_PATH_NOT_EDGE;
6066   }
6067
6068   list<SMESH_MeshEditor_PathPoint> fullList;
6069
6070   const TopoDS_Shape& aS = theTrack->GetSubShape();
6071   // Sub-shape for the Pattern must be an Edge or Wire
6072   if( aS.ShapeType() == TopAbs_EDGE ) {
6073     aTrackEdge = TopoDS::Edge( aS );
6074     // the Edge must not be degenerated
6075     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6076       return EXTR_BAD_PATH_SHAPE;
6077     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6078     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6079     const SMDS_MeshNode* aN1 = aItN->next();
6080     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6081     const SMDS_MeshNode* aN2 = aItN->next();
6082     // starting node must be aN1 or aN2
6083     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6084       return EXTR_BAD_STARTING_NODE;
6085     aItN = pSubMeshDS->GetNodes();
6086     while ( aItN->more() ) {
6087       const SMDS_MeshNode* pNode = aItN->next();
6088       const SMDS_EdgePosition* pEPos =
6089         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6090       double aT = pEPos->GetUParameter();
6091       aPrms.push_back( aT );
6092     }
6093     //Extrusion_Error err =
6094     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6095   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6096     list< SMESH_subMesh* > LSM;
6097     TopTools_SequenceOfShape Edges;
6098     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6099     while(itSM->more()) {
6100       SMESH_subMesh* SM = itSM->next();
6101       LSM.push_back(SM);
6102       const TopoDS_Shape& aS = SM->GetSubShape();
6103       Edges.Append(aS);
6104     }
6105     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6106     int startNid = theN1->GetID();
6107     TColStd_MapOfInteger UsedNums;
6108
6109     int NbEdges = Edges.Length();
6110     int i = 1;
6111     for(; i<=NbEdges; i++) {
6112       int k = 0;
6113       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6114       for(; itLSM!=LSM.end(); itLSM++) {
6115         k++;
6116         if(UsedNums.Contains(k)) continue;
6117         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6118         SMESH_subMesh* locTrack = *itLSM;
6119         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6120         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6121         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6122         const SMDS_MeshNode* aN1 = aItN->next();
6123         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6124         const SMDS_MeshNode* aN2 = aItN->next();
6125         // starting node must be aN1 or aN2
6126         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6127         // 2. Collect parameters on the track edge
6128         aPrms.clear();
6129         aItN = locMeshDS->GetNodes();
6130         while ( aItN->more() ) {
6131           const SMDS_MeshNode* pNode = aItN->next();
6132           const SMDS_EdgePosition* pEPos =
6133             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6134           double aT = pEPos->GetUParameter();
6135           aPrms.push_back( aT );
6136         }
6137         list<SMESH_MeshEditor_PathPoint> LPP;
6138         //Extrusion_Error err =
6139         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6140         LLPPs.push_back(LPP);
6141         UsedNums.Add(k);
6142         // update startN for search following egde
6143         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6144         else startNid = aN1->GetID();
6145         break;
6146       }
6147     }
6148     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6149     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6150     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6151     for(; itPP!=firstList.end(); itPP++) {
6152       fullList.push_back( *itPP );
6153     }
6154     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6155     fullList.pop_back();
6156     itLLPP++;
6157     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6158       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6159       itPP = currList.begin();
6160       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6161       gp_Dir D1 = PP1.Tangent();
6162       gp_Dir D2 = PP2.Tangent();
6163       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6164                            (D1.Z()+D2.Z())/2 ) );
6165       PP1.SetTangent(Dnew);
6166       fullList.push_back(PP1);
6167       itPP++;
6168       for(; itPP!=firstList.end(); itPP++) {
6169         fullList.push_back( *itPP );
6170       }
6171       PP1 = fullList.back();
6172       fullList.pop_back();
6173     }
6174     // if wire not closed
6175     fullList.push_back(PP1);
6176     // else ???
6177   }
6178   else {
6179     return EXTR_BAD_PATH_SHAPE;
6180   }
6181
6182   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6183                           theHasRefPoint, theRefPoint, theMakeGroups);
6184 }
6185
6186
6187 //=======================================================================
6188 //function : ExtrusionAlongTrack
6189 //purpose  :
6190 //=======================================================================
6191 SMESH_MeshEditor::Extrusion_Error
6192 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6193                                        SMESH_Mesh*          theTrack,
6194                                        const SMDS_MeshNode* theN1,
6195                                        const bool           theHasAngles,
6196                                        list<double>&        theAngles,
6197                                        const bool           theLinearVariation,
6198                                        const bool           theHasRefPoint,
6199                                        const gp_Pnt&        theRefPoint,
6200                                        const bool           theMakeGroups)
6201 {
6202   myLastCreatedElems.Clear();
6203   myLastCreatedNodes.Clear();
6204
6205   int aNbE;
6206   std::list<double> aPrms;
6207   TIDSortedElemSet::iterator itElem;
6208
6209   gp_XYZ aGC;
6210   TopoDS_Edge aTrackEdge;
6211   TopoDS_Vertex aV1, aV2;
6212
6213   SMDS_ElemIteratorPtr aItE;
6214   SMDS_NodeIteratorPtr aItN;
6215   SMDSAbs_ElementType aTypeE;
6216
6217   TNodeOfNodeListMap mapNewNodes;
6218
6219   // 1. Check data
6220   aNbE = theElements[0].size() + theElements[1].size();
6221   // nothing to do
6222   if ( !aNbE )
6223     return EXTR_NO_ELEMENTS;
6224
6225   // 1.1 Track Pattern
6226   ASSERT( theTrack );
6227
6228   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6229
6230   aItE = pMeshDS->elementsIterator();
6231   while ( aItE->more() ) {
6232     const SMDS_MeshElement* pE = aItE->next();
6233     aTypeE = pE->GetType();
6234     // Pattern must contain links only
6235     if ( aTypeE != SMDSAbs_Edge )
6236       return EXTR_PATH_NOT_EDGE;
6237   }
6238
6239   list<SMESH_MeshEditor_PathPoint> fullList;
6240
6241   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6242
6243   if ( !theTrack->HasShapeToMesh() ) {
6244     //Mesh without shape
6245     const SMDS_MeshNode* currentNode = NULL;
6246     const SMDS_MeshNode* prevNode = theN1;
6247     std::vector<const SMDS_MeshNode*> aNodesList;
6248     aNodesList.push_back(theN1);
6249     int nbEdges = 0, conn=0;
6250     const SMDS_MeshElement* prevElem = NULL;
6251     const SMDS_MeshElement* currentElem = NULL;
6252     int totalNbEdges = theTrack->NbEdges();
6253     SMDS_ElemIteratorPtr nIt;
6254
6255     //check start node
6256     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6257       return EXTR_BAD_STARTING_NODE;
6258     }
6259
6260     conn = nbEdgeConnectivity(theN1);
6261     if( conn != 1 )
6262       return EXTR_PATH_NOT_EDGE;
6263
6264     aItE = theN1->GetInverseElementIterator();
6265     prevElem = aItE->next();
6266     currentElem = prevElem;
6267     //Get all nodes
6268     if(totalNbEdges == 1 ) {
6269       nIt = currentElem->nodesIterator();
6270       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6271       if(currentNode == prevNode)
6272         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6273       aNodesList.push_back(currentNode);
6274     } else {
6275       nIt = currentElem->nodesIterator();
6276       while( nIt->more() ) {
6277         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6278         if(currentNode == prevNode)
6279           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6280         aNodesList.push_back(currentNode);
6281
6282         //case of the closed mesh
6283         if(currentNode == theN1) {
6284           nbEdges++;
6285           break;
6286         }
6287
6288         conn = nbEdgeConnectivity(currentNode);
6289         if(conn > 2) {
6290           return EXTR_PATH_NOT_EDGE;
6291         }else if( conn == 1 && nbEdges > 0 ) {
6292           //End of the path
6293           nbEdges++;
6294           break;
6295         }else {
6296           prevNode = currentNode;
6297           aItE = currentNode->GetInverseElementIterator();
6298           currentElem = aItE->next();
6299           if( currentElem  == prevElem)
6300             currentElem = aItE->next();
6301           nIt = currentElem->nodesIterator();
6302           prevElem = currentElem;
6303           nbEdges++;
6304         }
6305       }
6306     }
6307
6308     if(nbEdges != totalNbEdges)
6309       return EXTR_PATH_NOT_EDGE;
6310
6311     TopTools_SequenceOfShape Edges;
6312     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6313     int startNid = theN1->GetID();
6314     for ( size_t i = 1; i < aNodesList.size(); i++ )
6315     {
6316       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6317       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6318       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6319       list<SMESH_MeshEditor_PathPoint> LPP;
6320       aPrms.clear();
6321       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6322       LLPPs.push_back(LPP);
6323       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6324       else                                        startNid = aNodesList[i-1]->GetID();
6325     }
6326
6327     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6328     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6329     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6330     for(; itPP!=firstList.end(); itPP++) {
6331       fullList.push_back( *itPP );
6332     }
6333
6334     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6335     SMESH_MeshEditor_PathPoint PP2;
6336     fullList.pop_back();
6337     itLLPP++;
6338     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6339       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6340       itPP = currList.begin();
6341       PP2 = currList.front();
6342       gp_Dir D1 = PP1.Tangent();
6343       gp_Dir D2 = PP2.Tangent();
6344       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6345       PP1.SetTangent(Dnew);
6346       fullList.push_back(PP1);
6347       itPP++;
6348       for(; itPP!=currList.end(); itPP++) {
6349         fullList.push_back( *itPP );
6350       }
6351       PP1 = fullList.back();
6352       fullList.pop_back();
6353     }
6354     fullList.push_back(PP1);
6355
6356   } // Sub-shape for the Pattern must be an Edge or Wire
6357   else if ( aS.ShapeType() == TopAbs_EDGE )
6358   {
6359     aTrackEdge = TopoDS::Edge( aS );
6360     // the Edge must not be degenerated
6361     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6362       return EXTR_BAD_PATH_SHAPE;
6363     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6364     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6365     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6366     // starting node must be aN1 or aN2
6367     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6368       return EXTR_BAD_STARTING_NODE;
6369     aItN = pMeshDS->nodesIterator();
6370     while ( aItN->more() ) {
6371       const SMDS_MeshNode* pNode = aItN->next();
6372       if( pNode==aN1 || pNode==aN2 ) continue;
6373       const SMDS_EdgePosition* pEPos =
6374         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6375       double aT = pEPos->GetUParameter();
6376       aPrms.push_back( aT );
6377     }
6378     //Extrusion_Error err =
6379     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6380   }
6381   else if( aS.ShapeType() == TopAbs_WIRE ) {
6382     list< SMESH_subMesh* > LSM;
6383     TopTools_SequenceOfShape Edges;
6384     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6385     for(; eExp.More(); eExp.Next()) {
6386       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6387       if( SMESH_Algo::isDegenerated(E) ) continue;
6388       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6389       if(SM) {
6390         LSM.push_back(SM);
6391         Edges.Append(E);
6392       }
6393     }
6394     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6395     TopoDS_Vertex aVprev;
6396     TColStd_MapOfInteger UsedNums;
6397     int NbEdges = Edges.Length();
6398     int i = 1;
6399     for(; i<=NbEdges; i++) {
6400       int k = 0;
6401       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6402       for(; itLSM!=LSM.end(); itLSM++) {
6403         k++;
6404         if(UsedNums.Contains(k)) continue;
6405         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6406         SMESH_subMesh* locTrack = *itLSM;
6407         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6408         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6409         bool aN1isOK = false, aN2isOK = false;
6410         if ( aVprev.IsNull() ) {
6411           // if previous vertex is not yet defined, it means that we in the beginning of wire
6412           // and we have to find initial vertex corresponding to starting node theN1
6413           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6414           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6415           // starting node must be aN1 or aN2
6416           aN1isOK = ( aN1 && aN1 == theN1 );
6417           aN2isOK = ( aN2 && aN2 == theN1 );
6418         }
6419         else {
6420           // we have specified ending vertex of the previous edge on the previous iteration
6421           // and we have just to check that it corresponds to any vertex in current segment
6422           aN1isOK = aVprev.IsSame( aV1 );
6423           aN2isOK = aVprev.IsSame( aV2 );
6424         }
6425         if ( !aN1isOK && !aN2isOK ) continue;
6426         // 2. Collect parameters on the track edge
6427         aPrms.clear();
6428         aItN = locMeshDS->GetNodes();
6429         while ( aItN->more() ) {
6430           const SMDS_MeshNode*     pNode = aItN->next();
6431           const SMDS_EdgePosition* pEPos =
6432             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6433           double aT = pEPos->GetUParameter();
6434           aPrms.push_back( aT );
6435         }
6436         list<SMESH_MeshEditor_PathPoint> LPP;
6437         //Extrusion_Error err =
6438         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6439         LLPPs.push_back(LPP);
6440         UsedNums.Add(k);
6441         // update startN for search following egde
6442         if ( aN1isOK ) aVprev = aV2;
6443         else           aVprev = aV1;
6444         break;
6445       }
6446     }
6447     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6448     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6449     fullList.splice( fullList.end(), firstList );
6450
6451     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6452     fullList.pop_back();
6453     itLLPP++;
6454     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6455       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6456       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6457       gp_Dir D1 = PP1.Tangent();
6458       gp_Dir D2 = PP2.Tangent();
6459       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6460       PP1.SetTangent(Dnew);
6461       fullList.push_back(PP1);
6462       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6463       PP1 = fullList.back();
6464       fullList.pop_back();
6465     }
6466     // if wire not closed
6467     fullList.push_back(PP1);
6468     // else ???
6469   }
6470   else {
6471     return EXTR_BAD_PATH_SHAPE;
6472   }
6473
6474   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6475                           theHasRefPoint, theRefPoint, theMakeGroups);
6476 }
6477
6478
6479 //=======================================================================
6480 //function : makeEdgePathPoints
6481 //purpose  : auxiliary for ExtrusionAlongTrack
6482 //=======================================================================
6483 SMESH_MeshEditor::Extrusion_Error
6484 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6485                                      const TopoDS_Edge&                aTrackEdge,
6486                                      bool                              FirstIsStart,
6487                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6488 {
6489   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6490   aTolVec=1.e-7;
6491   aTolVec2=aTolVec*aTolVec;
6492   double aT1, aT2;
6493   TopoDS_Vertex aV1, aV2;
6494   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6495   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6496   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6497   // 2. Collect parameters on the track edge
6498   aPrms.push_front( aT1 );
6499   aPrms.push_back( aT2 );
6500   // sort parameters
6501   aPrms.sort();
6502   if( FirstIsStart ) {
6503     if ( aT1 > aT2 ) {
6504       aPrms.reverse();
6505     }
6506   }
6507   else {
6508     if ( aT2 > aT1 ) {
6509       aPrms.reverse();
6510     }
6511   }
6512   // 3. Path Points
6513   SMESH_MeshEditor_PathPoint aPP;
6514   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6515   std::list<double>::iterator aItD = aPrms.begin();
6516   for(; aItD != aPrms.end(); ++aItD) {
6517     double aT = *aItD;
6518     gp_Pnt aP3D;
6519     gp_Vec aVec;
6520     aC3D->D1( aT, aP3D, aVec );
6521     aL2 = aVec.SquareMagnitude();
6522     if ( aL2 < aTolVec2 )
6523       return EXTR_CANT_GET_TANGENT;
6524     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6525     aPP.SetPnt( aP3D );
6526     aPP.SetTangent( aTgt );
6527     aPP.SetParameter( aT );
6528     LPP.push_back(aPP);
6529   }
6530   return EXTR_OK;
6531 }
6532
6533
6534 //=======================================================================
6535 //function : makeExtrElements
6536 //purpose  : auxiliary for ExtrusionAlongTrack
6537 //=======================================================================
6538 SMESH_MeshEditor::Extrusion_Error
6539 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6540                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6541                                    const bool                        theHasAngles,
6542                                    list<double>&                     theAngles,
6543                                    const bool                        theLinearVariation,
6544                                    const bool                        theHasRefPoint,
6545                                    const gp_Pnt&                     theRefPoint,
6546                                    const bool                        theMakeGroups)
6547 {
6548   const int aNbTP = fullList.size();
6549
6550   // Angles
6551   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6552     linearAngleVariation(aNbTP-1, theAngles);
6553
6554   // fill vector of path points with angles
6555   vector<SMESH_MeshEditor_PathPoint> aPPs;
6556   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6557   list<double>::iterator                 itAngles = theAngles.begin();
6558   aPPs.push_back( *itPP++ );
6559   for( ; itPP != fullList.end(); itPP++) {
6560     aPPs.push_back( *itPP );
6561     if ( theHasAngles && itAngles != theAngles.end() )
6562       aPPs.back().SetAngle( *itAngles++ );
6563   }
6564
6565   TNodeOfNodeListMap   mapNewNodes;
6566   TElemOfVecOfNnlmiMap mapElemNewNodes;
6567   TTElemOfElemListMap  newElemsMap;
6568   TIDSortedElemSet::iterator itElem;
6569   // source elements for each generated one
6570   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6571
6572   // 3. Center of rotation aV0
6573   gp_Pnt aV0 = theRefPoint;
6574   if ( !theHasRefPoint )
6575   {
6576     gp_XYZ aGC( 0.,0.,0. );
6577     TIDSortedElemSet newNodes;
6578
6579     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6580     {
6581       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6582       itElem = theElements.begin();
6583       for ( ; itElem != theElements.end(); itElem++ )
6584       {
6585         const SMDS_MeshElement* elem = *itElem;
6586         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6587         while ( itN->more() ) {
6588           const SMDS_MeshElement* node = itN->next();
6589           if ( newNodes.insert( node ).second )
6590             aGC += SMESH_TNodeXYZ( node );
6591         }
6592       }
6593     }
6594     aGC /= newNodes.size();
6595     aV0.SetXYZ( aGC );
6596   } // if (!theHasRefPoint) {
6597
6598   // 4. Processing the elements
6599   SMESHDS_Mesh* aMesh = GetMeshDS();
6600   list<const SMDS_MeshNode*> emptyList;
6601
6602   setElemsFirst( theElemSets );
6603   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6604   {
6605     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6606     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6607     {
6608       const SMDS_MeshElement* elem = *itElem;
6609
6610       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6611       newNodesItVec.reserve( elem->NbNodes() );
6612
6613       // loop on elem nodes
6614       int nodeIndex = -1;
6615       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6616       while ( itN->more() )
6617       {
6618         ++nodeIndex;
6619         // check if a node has been already processed
6620         const SMDS_MeshNode* node = cast2Node( itN->next() );
6621         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6622         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6623         if ( listNewNodes.empty() )
6624         {
6625           // make new nodes
6626           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6627           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6628           gp_Ax1 anAx1, anAxT1T0;
6629           gp_Dir aDT1x, aDT0x, aDT1T0;
6630
6631           aTolAng=1.e-4;
6632
6633           aV0x = aV0;
6634           aPN0 = SMESH_TNodeXYZ( node );
6635
6636           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6637           aP0x = aPP0.Pnt();
6638           aDT0x= aPP0.Tangent();
6639
6640           for ( int j = 1; j < aNbTP; ++j ) {
6641             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6642             aP1x     = aPP1.Pnt();
6643             aDT1x    = aPP1.Tangent();
6644             aAngle1x = aPP1.Angle();
6645
6646             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6647             // Translation
6648             gp_Vec aV01x( aP0x, aP1x );
6649             aTrsf.SetTranslation( aV01x );
6650
6651             // traslated point
6652             aV1x = aV0x.Transformed( aTrsf );
6653             aPN1 = aPN0.Transformed( aTrsf );
6654
6655             // rotation 1 [ T1,T0 ]
6656             aAngleT1T0=-aDT1x.Angle( aDT0x );
6657             if (fabs(aAngleT1T0) > aTolAng)
6658             {
6659               aDT1T0=aDT1x^aDT0x;
6660               anAxT1T0.SetLocation( aV1x );
6661               anAxT1T0.SetDirection( aDT1T0 );
6662               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6663
6664               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6665             }
6666
6667             // rotation 2
6668             if ( theHasAngles ) {
6669               anAx1.SetLocation( aV1x );
6670               anAx1.SetDirection( aDT1x );
6671               aTrsfRot.SetRotation( anAx1, aAngle1x );
6672
6673               aPN1 = aPN1.Transformed( aTrsfRot );
6674             }
6675
6676             // make new node
6677             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6678             {
6679               // create additional node
6680               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6681               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6682               myLastCreatedNodes.Append(newNode);
6683               srcNodes.Append( node );
6684               listNewNodes.push_back( newNode );
6685             }
6686             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6687             myLastCreatedNodes.Append(newNode);
6688             srcNodes.Append( node );
6689             listNewNodes.push_back( newNode );
6690
6691             aPN0 = aPN1;
6692             aP0x = aP1x;
6693             aV0x = aV1x;
6694             aDT0x = aDT1x;
6695           }
6696         }
6697         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6698         {
6699           // if current elem is quadratic and current node is not medium
6700           // we have to check - may be it is needed to insert additional nodes
6701           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6702           if ((int) listNewNodes.size() == aNbTP-1 )
6703           {
6704             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6705             gp_XYZ P(node->X(), node->Y(), node->Z());
6706             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6707             int i;
6708             for(i=0; i<aNbTP-1; i++) {
6709               const SMDS_MeshNode* N = *it;
6710               double x = ( N->X() + P.X() )/2.;
6711               double y = ( N->Y() + P.Y() )/2.;
6712               double z = ( N->Z() + P.Z() )/2.;
6713               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6714               srcNodes.Append( node );
6715               myLastCreatedNodes.Append(newN);
6716               aNodes[2*i] = newN;
6717               aNodes[2*i+1] = N;
6718               P = gp_XYZ(N->X(),N->Y(),N->Z());
6719             }
6720             listNewNodes.clear();
6721             for(i=0; i<2*(aNbTP-1); i++) {
6722               listNewNodes.push_back(aNodes[i]);
6723             }
6724           }
6725         }
6726
6727         newNodesItVec.push_back( nIt );
6728       }
6729
6730       // make new elements
6731       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6732     }
6733   }
6734
6735   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6736
6737   if ( theMakeGroups )
6738     generateGroups( srcNodes, srcElems, "extruded");
6739
6740   return EXTR_OK;
6741 }
6742
6743
6744 //=======================================================================
6745 //function : linearAngleVariation
6746 //purpose  : spread values over nbSteps
6747 //=======================================================================
6748
6749 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6750                                             list<double>& Angles)
6751 {
6752   int nbAngles = Angles.size();
6753   if( nbSteps > nbAngles && nbAngles > 0 )
6754   {
6755     vector<double> theAngles(nbAngles);
6756     theAngles.assign( Angles.begin(), Angles.end() );
6757
6758     list<double> res;
6759     double rAn2St = double( nbAngles ) / double( nbSteps );
6760     double angPrev = 0, angle;
6761     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6762     {
6763       double angCur = rAn2St * ( iSt+1 );
6764       double angCurFloor  = floor( angCur );
6765       double angPrevFloor = floor( angPrev );
6766       if ( angPrevFloor == angCurFloor )
6767         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6768       else {
6769         int iP = int( angPrevFloor );
6770         double angPrevCeil = ceil(angPrev);
6771         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6772
6773         int iC = int( angCurFloor );
6774         if ( iC < nbAngles )
6775           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6776
6777         iP = int( angPrevCeil );
6778         while ( iC-- > iP )
6779           angle += theAngles[ iC ];
6780       }
6781       res.push_back(angle);
6782       angPrev = angCur;
6783     }
6784     Angles.swap( res );
6785   }
6786 }
6787
6788
6789 //================================================================================
6790 /*!
6791  * \brief Move or copy theElements applying theTrsf to their nodes
6792  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6793  *  \param theTrsf - transformation to apply
6794  *  \param theCopy - if true, create translated copies of theElems
6795  *  \param theMakeGroups - if true and theCopy, create translated groups
6796  *  \param theTargetMesh - mesh to copy translated elements into
6797  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6798  */
6799 //================================================================================
6800
6801 SMESH_MeshEditor::PGroupIDs
6802 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6803                              const gp_Trsf&     theTrsf,
6804                              const bool         theCopy,
6805                              const bool         theMakeGroups,
6806                              SMESH_Mesh*        theTargetMesh)
6807 {
6808   myLastCreatedElems.Clear();
6809   myLastCreatedNodes.Clear();
6810
6811   bool needReverse = false;
6812   string groupPostfix;
6813   switch ( theTrsf.Form() ) {
6814   case gp_PntMirror:
6815     needReverse = true;
6816     groupPostfix = "mirrored";
6817     break;
6818   case gp_Ax1Mirror:
6819     groupPostfix = "mirrored";
6820     break;
6821   case gp_Ax2Mirror:
6822     needReverse = true;
6823     groupPostfix = "mirrored";
6824     break;
6825   case gp_Rotation:
6826     groupPostfix = "rotated";
6827     break;
6828   case gp_Translation:
6829     groupPostfix = "translated";
6830     break;
6831   case gp_Scale:
6832     groupPostfix = "scaled";
6833     break;
6834   case gp_CompoundTrsf: // different scale by axis
6835     groupPostfix = "scaled";
6836     break;
6837   default:
6838     needReverse = false;
6839     groupPostfix = "transformed";
6840   }
6841
6842   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6843   SMESHDS_Mesh* aMesh    = GetMeshDS();
6844
6845   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6846   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6847   SMESH_MeshEditor::ElemFeatures elemType;
6848
6849   // map old node to new one
6850   TNodeNodeMap nodeMap;
6851
6852   // elements sharing moved nodes; those of them which have all
6853   // nodes mirrored but are not in theElems are to be reversed
6854   TIDSortedElemSet inverseElemSet;
6855
6856   // source elements for each generated one
6857   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6858
6859   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6860   TIDSortedElemSet orphanNode;
6861
6862   if ( theElems.empty() ) // transform the whole mesh
6863   {
6864     // add all elements
6865     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6866     while ( eIt->more() ) theElems.insert( eIt->next() );
6867     // add orphan nodes
6868     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6869     while ( nIt->more() )
6870     {
6871       const SMDS_MeshNode* node = nIt->next();
6872       if ( node->NbInverseElements() == 0)
6873         orphanNode.insert( node );
6874     }
6875   }
6876
6877   // loop on elements to transform nodes : first orphan nodes then elems
6878   TIDSortedElemSet::iterator itElem;
6879   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6880   for (int i=0; i<2; i++)
6881     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6882     {
6883       const SMDS_MeshElement* elem = *itElem;
6884       if ( !elem )
6885         continue;
6886
6887       // loop on elem nodes
6888       double coord[3];
6889       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6890       while ( itN->more() )
6891       {
6892         const SMDS_MeshNode* node = cast2Node( itN->next() );
6893         // check if a node has been already transformed
6894         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6895           nodeMap.insert( make_pair ( node, node ));
6896         if ( !n2n_isnew.second )
6897           continue;
6898
6899         node->GetXYZ( coord );
6900         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6901         if ( theTargetMesh ) {
6902           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6903           n2n_isnew.first->second = newNode;
6904           myLastCreatedNodes.Append(newNode);
6905           srcNodes.Append( node );
6906         }
6907         else if ( theCopy ) {
6908           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6909           n2n_isnew.first->second = newNode;
6910           myLastCreatedNodes.Append(newNode);
6911           srcNodes.Append( node );
6912         }
6913         else {
6914           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6915           // node position on shape becomes invalid
6916           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6917             ( SMDS_SpacePosition::originSpacePosition() );
6918         }
6919
6920         // keep inverse elements
6921         if ( !theCopy && !theTargetMesh && needReverse ) {
6922           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6923           while ( invElemIt->more() ) {
6924             const SMDS_MeshElement* iel = invElemIt->next();
6925             inverseElemSet.insert( iel );
6926           }
6927         }
6928       }
6929     } // loop on elems in { &orphanNode, &theElems };
6930
6931   // either create new elements or reverse mirrored ones
6932   if ( !theCopy && !needReverse && !theTargetMesh )
6933     return PGroupIDs();
6934
6935   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6936
6937   // Replicate or reverse elements
6938
6939   std::vector<int> iForw;
6940   vector<const SMDS_MeshNode*> nodes;
6941   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6942   {
6943     const SMDS_MeshElement* elem = *itElem;
6944     if ( !elem ) continue;
6945
6946     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6947     size_t               nbNodes  = elem->NbNodes();
6948     if ( geomType == SMDSGeom_NONE ) continue; // node
6949
6950     nodes.resize( nbNodes );
6951
6952     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6953     {
6954       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6955       if (!aPolyedre)
6956         continue;
6957       nodes.clear();
6958       bool allTransformed = true;
6959       int nbFaces = aPolyedre->NbFaces();
6960       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6961       {
6962         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6963         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6964         {
6965           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6966           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6967           if ( nodeMapIt == nodeMap.end() )
6968             allTransformed = false; // not all nodes transformed
6969           else
6970             nodes.push_back((*nodeMapIt).second);
6971         }
6972         if ( needReverse && allTransformed )
6973           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6974       }
6975       if ( !allTransformed )
6976         continue; // not all nodes transformed
6977     }
6978     else // ----------------------- the rest element types
6979     {
6980       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6981       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6982       const vector<int>&    i = needReverse ? iRev : iForw;
6983
6984       // find transformed nodes
6985       size_t iNode = 0;
6986       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6987       while ( itN->more() ) {
6988         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6989         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6990         if ( nodeMapIt == nodeMap.end() )
6991           break; // not all nodes transformed
6992         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6993       }
6994       if ( iNode != nbNodes )
6995         continue; // not all nodes transformed
6996     }
6997
6998     if ( editor ) {
6999       // copy in this or a new mesh
7000       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7001         srcElems.Append( elem );
7002     }
7003     else {
7004       // reverse element as it was reversed by transformation
7005       if ( nbNodes > 2 )
7006         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7007     }
7008
7009   } // loop on elements
7010
7011   if ( editor && editor != this )
7012     myLastCreatedElems = editor->myLastCreatedElems;
7013
7014   PGroupIDs newGroupIDs;
7015
7016   if ( ( theMakeGroups && theCopy ) ||
7017        ( theMakeGroups && theTargetMesh ) )
7018     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7019
7020   return newGroupIDs;
7021 }
7022
7023 //=======================================================================
7024 /*!
7025  * \brief Create groups of elements made during transformation
7026  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
7027  *  \param elemGens - elements making corresponding myLastCreatedElems
7028  *  \param postfix - to append to names of new groups
7029  *  \param targetMesh - mesh to create groups in
7030  *  \param topPresent - is there "top" elements that are created by sweeping
7031  */
7032 //=======================================================================
7033
7034 SMESH_MeshEditor::PGroupIDs
7035 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7036                                  const SMESH_SequenceOfElemPtr& elemGens,
7037                                  const std::string&             postfix,
7038                                  SMESH_Mesh*                    targetMesh,
7039                                  const bool                     topPresent)
7040 {
7041   PGroupIDs newGroupIDs( new list<int> );
7042   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7043
7044   // Sort existing groups by types and collect their names
7045
7046   // containers to store an old group and generated new ones;
7047   // 1st new group is for result elems of different type than a source one;
7048   // 2nd new group is for same type result elems ("top" group at extrusion)
7049   using boost::tuple;
7050   using boost::make_tuple;
7051   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7052   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7053   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7054   // group names
7055   set< string > groupNames;
7056
7057   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7058   if ( !groupIt->more() ) return newGroupIDs;
7059
7060   int newGroupID = mesh->GetGroupIds().back()+1;
7061   while ( groupIt->more() )
7062   {
7063     SMESH_Group * group = groupIt->next();
7064     if ( !group ) continue;
7065     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7066     if ( !groupDS || groupDS->IsEmpty() ) continue;
7067     groupNames.insert    ( group->GetName() );
7068     groupDS->SetStoreName( group->GetName() );
7069     const SMDSAbs_ElementType type = groupDS->GetType();
7070     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7071     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7072     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7073     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7074   }
7075
7076   // Loop on nodes and elements to add them in new groups
7077
7078   vector< const SMDS_MeshElement* > resultElems;
7079   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7080   {
7081     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7082     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7083     if ( gens.Length() != elems.Length() )
7084       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7085
7086     // loop on created elements
7087     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7088     {
7089       const SMDS_MeshElement* sourceElem = gens( iElem );
7090       if ( !sourceElem ) {
7091         MESSAGE("generateGroups(): NULL source element");
7092         continue;
7093       }
7094       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7095       if ( groupsOldNew.empty() ) { // no groups of this type at all
7096         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7097           ++iElem; // skip all elements made by sourceElem
7098         continue;
7099       }
7100       // collect all elements made by the iElem-th sourceElem
7101       resultElems.clear();
7102       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7103         if ( resElem != sourceElem )
7104           resultElems.push_back( resElem );
7105       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7106         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7107           if ( resElem != sourceElem )
7108             resultElems.push_back( resElem );
7109
7110       const SMDS_MeshElement* topElem = 0;
7111       if ( isNodes ) // there must be a top element
7112       {
7113         topElem = resultElems.back();
7114         resultElems.pop_back();
7115       }
7116       else
7117       {
7118         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7119         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7120           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7121           {
7122             topElem = *resElemIt;
7123             *resElemIt = 0; // erase *resElemIt
7124             break;
7125           }
7126       }
7127       // add resultElems to groups originted from ones the sourceElem belongs to
7128       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7129       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7130       {
7131         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7132         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7133         {
7134           // fill in a new group
7135           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7136           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7137           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7138             if ( *resElemIt )
7139               newGroup.Add( *resElemIt );
7140
7141           // fill a "top" group
7142           if ( topElem )
7143           {
7144             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7145             newTopGroup.Add( topElem );
7146          }
7147         }
7148       }
7149     } // loop on created elements
7150   }// loop on nodes and elements
7151
7152   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7153
7154   list<int> topGrouIds;
7155   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7156   {
7157     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7158     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7159                                       orderedOldNewGroups[i]->get<2>() };
7160     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7161     {
7162       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7163       if ( newGroupDS->IsEmpty() )
7164       {
7165         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7166       }
7167       else
7168       {
7169         // set group type
7170         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7171
7172         // make a name
7173         const bool isTop = ( topPresent &&
7174                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7175                              is2nd );
7176
7177         string name = oldGroupDS->GetStoreName();
7178         { // remove trailing whitespaces (issue 22599)
7179           size_t size = name.size();
7180           while ( size > 1 && isspace( name[ size-1 ]))
7181             --size;
7182           if ( size != name.size() )
7183           {
7184             name.resize( size );
7185             oldGroupDS->SetStoreName( name.c_str() );
7186           }
7187         }
7188         if ( !targetMesh ) {
7189           string suffix = ( isTop ? "top": postfix.c_str() );
7190           name += "_";
7191           name += suffix;
7192           int nb = 1;
7193           while ( !groupNames.insert( name ).second ) // name exists
7194             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7195         }
7196         else if ( isTop ) {
7197           name += "_top";
7198         }
7199         newGroupDS->SetStoreName( name.c_str() );
7200
7201         // make a SMESH_Groups
7202         mesh->AddGroup( newGroupDS );
7203         if ( isTop )
7204           topGrouIds.push_back( newGroupDS->GetID() );
7205         else
7206           newGroupIDs->push_back( newGroupDS->GetID() );
7207       }
7208     }
7209   }
7210   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7211
7212   return newGroupIDs;
7213 }
7214
7215 //================================================================================
7216 /*!
7217  *  * \brief Return list of group of nodes close to each other within theTolerance
7218  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7219  *  *        an Octree algorithm
7220  *  \param [in,out] theNodes - the nodes to treat
7221  *  \param [in]     theTolerance - the tolerance
7222  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7223  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7224  *         corner and medium nodes in separate groups
7225  */
7226 //================================================================================
7227
7228 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7229                                             const double         theTolerance,
7230                                             TListOfListOfNodes & theGroupsOfNodes,
7231                                             bool                 theSeparateCornersAndMedium)
7232 {
7233   myLastCreatedElems.Clear();
7234   myLastCreatedNodes.Clear();
7235
7236   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7237        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7238        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7239     theSeparateCornersAndMedium = false;
7240
7241   TIDSortedNodeSet& corners = theNodes;
7242   TIDSortedNodeSet  medium;
7243
7244   if ( theNodes.empty() ) // get all nodes in the mesh
7245   {
7246     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7247     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7248     if ( theSeparateCornersAndMedium )
7249       while ( nIt->more() )
7250       {
7251         const SMDS_MeshNode* n = nIt->next();
7252         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7253         nodeSet->insert( nodeSet->end(), n );
7254       }
7255     else
7256       while ( nIt->more() )
7257         theNodes.insert( theNodes.end(), nIt->next() );
7258   }
7259   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7260   {
7261     TIDSortedNodeSet::iterator nIt = corners.begin();
7262     while ( nIt != corners.end() )
7263       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7264       {
7265         medium.insert( medium.end(), *nIt );
7266         corners.erase( nIt++ );
7267       }
7268       else
7269       {
7270         ++nIt;
7271       }
7272   }
7273
7274   if ( !corners.empty() )
7275     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7276   if ( !medium.empty() )
7277     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7278 }
7279
7280 //=======================================================================
7281 //function : SimplifyFace
7282 //purpose  : split a chain of nodes into several closed chains
7283 //=======================================================================
7284
7285 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7286                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7287                                     vector<int>&                         quantities) const
7288 {
7289   int nbNodes = faceNodes.size();
7290   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7291     --nbNodes;
7292   if ( nbNodes < 3 )
7293     return 0;
7294   size_t prevNbQuant = quantities.size();
7295
7296   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7297   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7298   map< const SMDS_MeshNode*, int >::iterator nInd;
7299
7300   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7301   simpleNodes.push_back( faceNodes[0] );
7302   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7303   {
7304     if ( faceNodes[ iCur ] != simpleNodes.back() )
7305     {
7306       int index = simpleNodes.size();
7307       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7308       int prevIndex = nInd->second;
7309       if ( prevIndex < index )
7310       {
7311         // a sub-loop found
7312         int loopLen = index - prevIndex;
7313         if ( loopLen > 2 )
7314         {
7315           // store the sub-loop
7316           quantities.push_back( loopLen );
7317           for ( int i = prevIndex; i < index; i++ )
7318             poly_nodes.push_back( simpleNodes[ i ]);
7319         }
7320         simpleNodes.resize( prevIndex+1 );
7321       }
7322       else
7323       {
7324         simpleNodes.push_back( faceNodes[ iCur ]);
7325       }
7326     }
7327   }
7328
7329   if ( simpleNodes.size() > 2 )
7330   {
7331     quantities.push_back( simpleNodes.size() );
7332     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7333   }
7334
7335   return quantities.size() - prevNbQuant;
7336 }
7337
7338 //=======================================================================
7339 //function : MergeNodes
7340 //purpose  : In each group, the cdr of nodes are substituted by the first one
7341 //           in all elements.
7342 //=======================================================================
7343
7344 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7345                                    const bool           theAvoidMakingHoles)
7346 {
7347   myLastCreatedElems.Clear();
7348   myLastCreatedNodes.Clear();
7349
7350   SMESHDS_Mesh* mesh = GetMeshDS();
7351
7352   TNodeNodeMap nodeNodeMap; // node to replace - new node
7353   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7354   list< int > rmElemIds, rmNodeIds;
7355   vector< ElemFeatures > newElemDefs;
7356
7357   // Fill nodeNodeMap and elems
7358
7359   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7360   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7361   {
7362     list<const SMDS_MeshNode*>& nodes = *grIt;
7363     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7364     const SMDS_MeshNode* nToKeep = *nIt;
7365     for ( ++nIt; nIt != nodes.end(); nIt++ )
7366     {
7367       const SMDS_MeshNode* nToRemove = *nIt;
7368       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7369       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7370       while ( invElemIt->more() ) {
7371         const SMDS_MeshElement* elem = invElemIt->next();
7372         elems.insert(elem);
7373       }
7374     }
7375   }
7376
7377   // Apply recursive replacements (BUG 0020185)
7378   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7379   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7380   {
7381     const SMDS_MeshNode* nToKeep = nnIt->second;
7382     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7383     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7384       nToKeep = nnIt_i->second;
7385     nnIt->second = nToKeep;
7386   }
7387
7388   if ( theAvoidMakingHoles )
7389   {
7390     // find elements whose topology changes
7391
7392     vector<const SMDS_MeshElement*> pbElems;
7393     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7394     for ( ; eIt != elems.end(); ++eIt )
7395     {
7396       const SMDS_MeshElement* elem = *eIt;
7397       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7398       while ( itN->more() )
7399       {
7400         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7401         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7402         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7403         {
7404           // several nodes of elem stick
7405           pbElems.push_back( elem );
7406           break;
7407         }
7408       }
7409     }
7410     // exclude from merge nodes causing spoiling element
7411     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7412     {
7413       bool nodesExcluded = false;
7414       for ( size_t i = 0; i < pbElems.size(); ++i )
7415       {
7416         size_t prevNbMergeNodes = nodeNodeMap.size();
7417         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7418              prevNbMergeNodes < nodeNodeMap.size() )
7419           nodesExcluded = true;
7420       }
7421       if ( !nodesExcluded )
7422         break;
7423     }
7424   }
7425
7426   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7427   {
7428     const SMDS_MeshNode* nToRemove = nnIt->first;
7429     const SMDS_MeshNode* nToKeep   = nnIt->second;
7430     if ( nToRemove != nToKeep )
7431     {
7432       rmNodeIds.push_back( nToRemove->GetID() );
7433       AddToSameGroups( nToKeep, nToRemove, mesh );
7434       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7435       // w/o creating node in place of merged ones.
7436       const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7437       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7438         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7439           sm->SetIsAlwaysComputed( true );
7440     }
7441   }
7442
7443   // Change element nodes or remove an element
7444
7445   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7446   for ( ; eIt != elems.end(); eIt++ )
7447   {
7448     const SMDS_MeshElement* elem = *eIt;
7449     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7450
7451     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7452     if ( !keepElem )
7453       rmElemIds.push_back( elem->GetID() );
7454
7455     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7456     {
7457       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7458                                                & newElemDefs[i].myNodes[0],
7459                                                newElemDefs[i].myNodes.size() ))
7460       {
7461         if ( i == 0 )
7462         {
7463           newElemDefs[i].SetID( elem->GetID() );
7464           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7465           if ( !keepElem ) rmElemIds.pop_back();
7466         }
7467         else
7468         {
7469           newElemDefs[i].SetID( -1 );
7470         }
7471         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7472         if ( sm && newElem )
7473           sm->AddElement( newElem );
7474         if ( elem != newElem )
7475           ReplaceElemInGroups( elem, newElem, mesh );
7476       }
7477     }
7478   }
7479
7480   // Remove bad elements, then equal nodes (order important)
7481   Remove( rmElemIds, /*isNodes=*/false );
7482   Remove( rmNodeIds, /*isNodes=*/true );
7483
7484   return;
7485 }
7486
7487 //=======================================================================
7488 //function : applyMerge
7489 //purpose  : Compute new connectivity of an element after merging nodes
7490 //  \param [in] elems - the element
7491 //  \param [out] newElemDefs - definition(s) of result element(s)
7492 //  \param [inout] nodeNodeMap - nodes to merge
7493 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7494 //              after merging (but not degenerated), removes nodes causing
7495 //              the invalidity from \a nodeNodeMap.
7496 //  \return bool - true if the element should be removed
7497 //=======================================================================
7498
7499 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7500                                    vector< ElemFeatures >& newElemDefs,
7501                                    TNodeNodeMap&           nodeNodeMap,
7502                                    const bool              avoidMakingHoles )
7503 {
7504   bool toRemove = false; // to remove elem
7505   int nbResElems = 1;    // nb new elements
7506
7507   newElemDefs.resize(nbResElems);
7508   newElemDefs[0].Init( elem );
7509   newElemDefs[0].myNodes.clear();
7510
7511   set<const SMDS_MeshNode*> nodeSet;
7512   vector< const SMDS_MeshNode*>   curNodes;
7513   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7514   vector<int> iRepl;
7515
7516   const        int  nbNodes = elem->NbNodes();
7517   SMDSAbs_EntityType entity = elem->GetEntityType();
7518
7519   curNodes.resize( nbNodes );
7520   uniqueNodes.resize( nbNodes );
7521   iRepl.resize( nbNodes );
7522   int iUnique = 0, iCur = 0, nbRepl = 0;
7523
7524   // Get new seq of nodes
7525
7526   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7527   while ( itN->more() )
7528   {
7529     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7530
7531     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7532     if ( nnIt != nodeNodeMap.end() ) {
7533       n = (*nnIt).second;
7534     }
7535     curNodes[ iCur ] = n;
7536     bool isUnique = nodeSet.insert( n ).second;
7537     if ( isUnique )
7538       uniqueNodes[ iUnique++ ] = n;
7539     else
7540       iRepl[ nbRepl++ ] = iCur;
7541     iCur++;
7542   }
7543
7544   // Analyse element topology after replacement
7545
7546   int nbUniqueNodes = nodeSet.size();
7547   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7548   {
7549     toRemove = true;
7550     nbResElems = 0;
7551
7552     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7553     {
7554       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7555       int nbCorners = nbNodes / 2;
7556       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7557       {
7558         int iNext = ( iCur + 1 ) % nbCorners;
7559         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7560         {
7561           int iMedium = iCur + nbCorners;
7562           vector< const SMDS_MeshNode* >::iterator i =
7563             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7564                        uniqueNodes.end(),
7565                        curNodes[ iMedium ]);
7566           if ( i != uniqueNodes.end() )
7567           {
7568             --nbUniqueNodes;
7569             for ( ; i+1 != uniqueNodes.end(); ++i )
7570               *i = *(i+1);
7571           }
7572         }
7573       }
7574     }
7575
7576     switch ( entity )
7577     {
7578     case SMDSEntity_Polygon:
7579     case SMDSEntity_Quad_Polygon: // Polygon
7580     {
7581       ElemFeatures* elemType = & newElemDefs[0];
7582       const bool isQuad = elemType->myIsQuad;
7583       if ( isQuad )
7584         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7585           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7586
7587       // a polygon can divide into several elements
7588       vector<const SMDS_MeshNode *> polygons_nodes;
7589       vector<int> quantities;
7590       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7591       newElemDefs.resize( nbResElems );
7592       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7593       {
7594         ElemFeatures* elemType = & newElemDefs[iface];
7595         if ( iface ) elemType->Init( elem );
7596
7597         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7598         int nbNewNodes = quantities[iface];
7599         face_nodes.assign( polygons_nodes.begin() + inode,
7600                            polygons_nodes.begin() + inode + nbNewNodes );
7601         inode += nbNewNodes;
7602         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7603         {
7604           bool isValid = ( nbNewNodes % 2 == 0 );
7605           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7606             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7607           elemType->SetQuad( isValid );
7608           if ( isValid ) // put medium nodes after corners
7609             SMDS_MeshCell::applyInterlaceRev
7610               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7611                                                     nbNewNodes ), face_nodes );
7612         }
7613         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7614       }
7615       nbUniqueNodes = newElemDefs[0].myNodes.size();
7616       break;
7617     } // Polygon
7618
7619     case SMDSEntity_Polyhedra: // Polyhedral volume
7620     {
7621       if ( nbUniqueNodes >= 4 )
7622       {
7623         // each face has to be analyzed in order to check volume validity
7624         if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7625         {
7626           int nbFaces = aPolyedre->NbFaces();
7627
7628           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7629           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7630           vector<const SMDS_MeshNode *>  faceNodes;
7631           poly_nodes.clear();
7632           quantities.clear();
7633
7634           for (int iface = 1; iface <= nbFaces; iface++)
7635           {
7636             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7637             faceNodes.resize( nbFaceNodes );
7638             for (int inode = 1; inode <= nbFaceNodes; inode++)
7639             {
7640               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7641               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7642               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7643                 faceNode = (*nnIt).second;
7644               faceNodes[inode - 1] = faceNode;
7645             }
7646             SimplifyFace(faceNodes, poly_nodes, quantities);
7647           }
7648
7649           if ( quantities.size() > 3 )
7650           {
7651             // TODO: remove coincident faces
7652             nbResElems = 1;
7653             nbUniqueNodes = newElemDefs[0].myNodes.size();
7654           }
7655         }
7656       }
7657     }
7658     break;
7659
7660     // Regular elements
7661     // TODO not all the possible cases are solved. Find something more generic?
7662     case SMDSEntity_Edge: //////// EDGE
7663     case SMDSEntity_Triangle: //// TRIANGLE
7664     case SMDSEntity_Quad_Triangle:
7665     case SMDSEntity_Tetra:
7666     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7667     {
7668       break;
7669     }
7670     case SMDSEntity_Quad_Edge:
7671     {
7672       break;
7673     }
7674     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7675     {
7676       if ( nbUniqueNodes < 3 )
7677         toRemove = true;
7678       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7679         toRemove = true; // opposite nodes stick
7680       else
7681         toRemove = false;
7682       break;
7683     }
7684     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7685     {
7686       //   1    5    2
7687       //    +---+---+
7688       //    |       |
7689       //   4+       +6
7690       //    |       |
7691       //    +---+---+
7692       //   0    7    3
7693       if ( nbUniqueNodes == 6 &&
7694            iRepl[0] < 4       &&
7695            ( nbRepl == 1 || iRepl[1] >= 4 ))
7696       {
7697         toRemove = false;
7698       }
7699       break;
7700     }
7701     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7702     {
7703       //   1    5    2
7704       //    +---+---+
7705       //    |       |
7706       //   4+  8+   +6
7707       //    |       |
7708       //    +---+---+
7709       //   0    7    3
7710       if ( nbUniqueNodes == 7 &&
7711            iRepl[0] < 4       &&
7712            ( nbRepl == 1 || iRepl[1] != 8 ))
7713       {
7714         toRemove = false;
7715       }
7716       break;
7717     }
7718     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7719     {
7720       if ( nbUniqueNodes == 4 ) {
7721         // ---------------------------------> tetrahedron
7722         if ( curNodes[3] == curNodes[4] &&
7723              curNodes[3] == curNodes[5] ) {
7724           // top nodes stick
7725           toRemove = false;
7726         }
7727         else if ( curNodes[0] == curNodes[1] &&
7728                   curNodes[0] == curNodes[2] ) {
7729           // bottom nodes stick: set a top before
7730           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7731           uniqueNodes[ 0 ] = curNodes [ 5 ];
7732           uniqueNodes[ 1 ] = curNodes [ 4 ];
7733           uniqueNodes[ 2 ] = curNodes [ 3 ];
7734           toRemove = false;
7735         }
7736         else if (( curNodes[0] == curNodes[3] ) +
7737                  ( curNodes[1] == curNodes[4] ) +
7738                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7739           // a lateral face turns into a line
7740           toRemove = false;
7741         }
7742       }
7743       else if ( nbUniqueNodes == 5 ) {
7744         // PENTAHEDRON --------------------> pyramid
7745         if ( curNodes[0] == curNodes[3] )
7746         {
7747           uniqueNodes[ 0 ] = curNodes[ 1 ];
7748           uniqueNodes[ 1 ] = curNodes[ 4 ];
7749           uniqueNodes[ 2 ] = curNodes[ 5 ];
7750           uniqueNodes[ 3 ] = curNodes[ 2 ];
7751           uniqueNodes[ 4 ] = curNodes[ 0 ];
7752           toRemove = false;
7753         }
7754         if ( curNodes[1] == curNodes[4] )
7755         {
7756           uniqueNodes[ 0 ] = curNodes[ 0 ];
7757           uniqueNodes[ 1 ] = curNodes[ 2 ];
7758           uniqueNodes[ 2 ] = curNodes[ 5 ];
7759           uniqueNodes[ 3 ] = curNodes[ 3 ];
7760           uniqueNodes[ 4 ] = curNodes[ 1 ];
7761           toRemove = false;
7762         }
7763         if ( curNodes[2] == curNodes[5] )
7764         {
7765           uniqueNodes[ 0 ] = curNodes[ 0 ];
7766           uniqueNodes[ 1 ] = curNodes[ 3 ];
7767           uniqueNodes[ 2 ] = curNodes[ 4 ];
7768           uniqueNodes[ 3 ] = curNodes[ 1 ];
7769           uniqueNodes[ 4 ] = curNodes[ 2 ];
7770           toRemove = false;
7771         }
7772       }
7773       break;
7774     }
7775     case SMDSEntity_Hexa:
7776     {
7777       //////////////////////////////////// HEXAHEDRON
7778       SMDS_VolumeTool hexa (elem);
7779       hexa.SetExternalNormal();
7780       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7781         //////////////////////// HEX ---> tetrahedron
7782         for ( int iFace = 0; iFace < 6; iFace++ ) {
7783           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7784           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7785               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7786               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7787             // one face turns into a point ...
7788             int  pickInd = ind[ 0 ];
7789             int iOppFace = hexa.GetOppFaceIndex( iFace );
7790             ind = hexa.GetFaceNodesIndices( iOppFace );
7791             int nbStick = 0;
7792             uniqueNodes.clear();
7793             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7794               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7795                 nbStick++;
7796               else
7797                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7798             }
7799             if ( nbStick == 1 ) {
7800               // ... and the opposite one - into a triangle.
7801               // set a top node
7802               uniqueNodes.push_back( curNodes[ pickInd ]);
7803               toRemove = false;
7804             }
7805             break;
7806           }
7807         }
7808       }
7809       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7810         //////////////////////// HEX ---> prism
7811         int nbTria = 0, iTria[3];
7812         const int *ind; // indices of face nodes
7813         // look for triangular faces
7814         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7815           ind = hexa.GetFaceNodesIndices( iFace );
7816           TIDSortedNodeSet faceNodes;
7817           for ( iCur = 0; iCur < 4; iCur++ )
7818             faceNodes.insert( curNodes[ind[iCur]] );
7819           if ( faceNodes.size() == 3 )
7820             iTria[ nbTria++ ] = iFace;
7821         }
7822         // check if triangles are opposite
7823         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7824         {
7825           // set nodes of the bottom triangle
7826           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7827           vector<int> indB;
7828           for ( iCur = 0; iCur < 4; iCur++ )
7829             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7830               indB.push_back( ind[iCur] );
7831           if ( !hexa.IsForward() )
7832             std::swap( indB[0], indB[2] );
7833           for ( iCur = 0; iCur < 3; iCur++ )
7834             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7835           // set nodes of the top triangle
7836           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7837           for ( iCur = 0; iCur < 3; ++iCur )
7838             for ( int j = 0; j < 4; ++j )
7839               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7840               {
7841                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7842                 break;
7843               }
7844           toRemove = false;
7845           break;
7846         }
7847       }
7848       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7849         //////////////////// HEXAHEDRON ---> pyramid
7850         for ( int iFace = 0; iFace < 6; iFace++ ) {
7851           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7852           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7853               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7854               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7855             // one face turns into a point ...
7856             int iOppFace = hexa.GetOppFaceIndex( iFace );
7857             ind = hexa.GetFaceNodesIndices( iOppFace );
7858             uniqueNodes.clear();
7859             for ( iCur = 0; iCur < 4; iCur++ ) {
7860               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7861                 break;
7862               else
7863                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7864             }
7865             if ( uniqueNodes.size() == 4 ) {
7866               // ... and the opposite one is a quadrangle
7867               // set a top node
7868               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7869               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7870               toRemove = false;
7871             }
7872             break;
7873           }
7874         }
7875       }
7876
7877       if ( toRemove && nbUniqueNodes > 4 ) {
7878         ////////////////// HEXAHEDRON ---> polyhedron
7879         hexa.SetExternalNormal();
7880         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7881         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7882         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7883         quantities.reserve( 6 );     quantities.clear();
7884         for ( int iFace = 0; iFace < 6; iFace++ )
7885         {
7886           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7887           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7888                curNodes[ind[1]] == curNodes[ind[3]] )
7889           {
7890             quantities.clear();
7891             break; // opposite nodes stick
7892           }
7893           nodeSet.clear();
7894           for ( iCur = 0; iCur < 4; iCur++ )
7895           {
7896             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7897               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7898           }
7899           if ( nodeSet.size() < 3 )
7900             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7901           else
7902             quantities.push_back( nodeSet.size() );
7903         }
7904         if ( quantities.size() >= 4 )
7905         {
7906           nbResElems = 1;
7907           nbUniqueNodes = poly_nodes.size();
7908           newElemDefs[0].SetPoly(true);
7909         }
7910       }
7911       break;
7912     } // case HEXAHEDRON
7913
7914     default:
7915       toRemove = true;
7916
7917     } // switch ( entity )
7918
7919     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7920     {
7921       // erase from nodeNodeMap nodes whose merge spoils elem
7922       vector< const SMDS_MeshNode* > noMergeNodes;
7923       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7924       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7925         nodeNodeMap.erase( noMergeNodes[i] );
7926     }
7927     
7928   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7929
7930   uniqueNodes.resize( nbUniqueNodes );
7931
7932   if ( !toRemove && nbResElems == 0 )
7933     nbResElems = 1;
7934
7935   newElemDefs.resize( nbResElems );
7936
7937   return !toRemove;
7938 }
7939
7940
7941 // ========================================================
7942 // class   : SortableElement
7943 // purpose : allow sorting elements basing on their nodes
7944 // ========================================================
7945 class SortableElement : public set <const SMDS_MeshElement*>
7946 {
7947 public:
7948
7949   SortableElement( const SMDS_MeshElement* theElem )
7950   {
7951     myElem = theElem;
7952     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7953     while ( nodeIt->more() )
7954       this->insert( nodeIt->next() );
7955   }
7956
7957   const SMDS_MeshElement* Get() const
7958   { return myElem; }
7959
7960 private:
7961   mutable const SMDS_MeshElement* myElem;
7962 };
7963
7964 //=======================================================================
7965 //function : FindEqualElements
7966 //purpose  : Return list of group of elements built on the same nodes.
7967 //           Search among theElements or in the whole mesh if theElements is empty
7968 //=======================================================================
7969
7970 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7971                                          TListOfListOfElementsID & theGroupsOfElementsID)
7972 {
7973   myLastCreatedElems.Clear();
7974   myLastCreatedNodes.Clear();
7975
7976   typedef map< SortableElement, int > TMapOfNodeSet;
7977   typedef list<int> TGroupOfElems;
7978
7979   if ( theElements.empty() )
7980   { // get all elements in the mesh
7981     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7982     while ( eIt->more() )
7983       theElements.insert( theElements.end(), eIt->next() );
7984   }
7985
7986   vector< TGroupOfElems > arrayOfGroups;
7987   TGroupOfElems groupOfElems;
7988   TMapOfNodeSet mapOfNodeSet;
7989
7990   TIDSortedElemSet::iterator elemIt = theElements.begin();
7991   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7992   {
7993     const SMDS_MeshElement* curElem = *elemIt;
7994     SortableElement SE(curElem);
7995     // check uniqueness
7996     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7997     if ( !pp.second ) { // one more coincident elem
7998       TMapOfNodeSet::iterator& itSE = pp.first;
7999       int ind = (*itSE).second;
8000       arrayOfGroups[ind].push_back( curElem->GetID() );
8001     }
8002     else {
8003       arrayOfGroups.push_back( groupOfElems );
8004       arrayOfGroups.back().push_back( curElem->GetID() );
8005       i++;
8006     }
8007   }
8008
8009   groupOfElems.clear();
8010   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8011   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8012   {
8013     if ( groupIt->size() > 1 ) {
8014       //groupOfElems.sort(); -- theElements is sorted already
8015       theGroupsOfElementsID.push_back( groupOfElems );
8016       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8017     }
8018   }
8019 }
8020
8021 //=======================================================================
8022 //function : MergeElements
8023 //purpose  : In each given group, substitute all elements by the first one.
8024 //=======================================================================
8025
8026 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8027 {
8028   myLastCreatedElems.Clear();
8029   myLastCreatedNodes.Clear();
8030
8031   typedef list<int> TListOfIDs;
8032   TListOfIDs rmElemIds; // IDs of elems to remove
8033
8034   SMESHDS_Mesh* aMesh = GetMeshDS();
8035
8036   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8037   while ( groupsIt != theGroupsOfElementsID.end() ) {
8038     TListOfIDs& aGroupOfElemID = *groupsIt;
8039     aGroupOfElemID.sort();
8040     int elemIDToKeep = aGroupOfElemID.front();
8041     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8042     aGroupOfElemID.pop_front();
8043     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8044     while ( idIt != aGroupOfElemID.end() ) {
8045       int elemIDToRemove = *idIt;
8046       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8047       // add the kept element in groups of removed one (PAL15188)
8048       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8049       rmElemIds.push_back( elemIDToRemove );
8050       ++idIt;
8051     }
8052     ++groupsIt;
8053   }
8054
8055   Remove( rmElemIds, false );
8056 }
8057
8058 //=======================================================================
8059 //function : MergeEqualElements
8060 //purpose  : Remove all but one of elements built on the same nodes.
8061 //=======================================================================
8062
8063 void SMESH_MeshEditor::MergeEqualElements()
8064 {
8065   TIDSortedElemSet aMeshElements; /* empty input ==
8066                                      to merge equal elements in the whole mesh */
8067   TListOfListOfElementsID aGroupsOfElementsID;
8068   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8069   MergeElements(aGroupsOfElementsID);
8070 }
8071
8072 //=======================================================================
8073 //function : findAdjacentFace
8074 //purpose  :
8075 //=======================================================================
8076
8077 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8078                                                 const SMDS_MeshNode* n2,
8079                                                 const SMDS_MeshElement* elem)
8080 {
8081   TIDSortedElemSet elemSet, avoidSet;
8082   if ( elem )
8083     avoidSet.insert ( elem );
8084   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8085 }
8086
8087 //=======================================================================
8088 //function : findSegment
8089 //purpose  : Return a mesh segment by two nodes one of which can be medium
8090 //=======================================================================
8091
8092 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8093                                            const SMDS_MeshNode* n2)
8094 {
8095   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8096   while ( it->more() )
8097   {
8098     const SMDS_MeshElement* seg = it->next();
8099     if ( seg->GetNodeIndex( n2 ) >= 0 )
8100       return seg;
8101   }
8102   return 0;
8103 }
8104
8105 //=======================================================================
8106 //function : FindFreeBorder
8107 //purpose  :
8108 //=======================================================================
8109
8110 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8111
8112 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8113                                        const SMDS_MeshNode*             theSecondNode,
8114                                        const SMDS_MeshNode*             theLastNode,
8115                                        list< const SMDS_MeshNode* > &   theNodes,
8116                                        list< const SMDS_MeshElement* >& theFaces)
8117 {
8118   if ( !theFirstNode || !theSecondNode )
8119     return false;
8120   // find border face between theFirstNode and theSecondNode
8121   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8122   if ( !curElem )
8123     return false;
8124
8125   theFaces.push_back( curElem );
8126   theNodes.push_back( theFirstNode );
8127   theNodes.push_back( theSecondNode );
8128
8129   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8130   TIDSortedElemSet foundElems;
8131   bool needTheLast = ( theLastNode != 0 );
8132
8133   while ( nStart != theLastNode ) {
8134     if ( nStart == theFirstNode )
8135       return !needTheLast;
8136
8137     // find all free border faces sharing form nStart
8138
8139     list< const SMDS_MeshElement* > curElemList;
8140     list< const SMDS_MeshNode* >    nStartList;
8141     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8142     while ( invElemIt->more() ) {
8143       const SMDS_MeshElement* e = invElemIt->next();
8144       if ( e == curElem || foundElems.insert( e ).second ) {
8145         // get nodes
8146         int iNode = 0, nbNodes = e->NbNodes();
8147         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8148
8149         if ( e->IsQuadratic() ) {
8150           const SMDS_VtkFace* F =
8151             dynamic_cast<const SMDS_VtkFace*>(e);
8152           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8153           // use special nodes iterator
8154           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8155           while( anIter->more() ) {
8156             nodes[ iNode++ ] = cast2Node(anIter->next());
8157           }
8158         }
8159         else {
8160           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8161           while ( nIt->more() )
8162             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8163         }
8164         nodes[ iNode ] = nodes[ 0 ];
8165         // check 2 links
8166         for ( iNode = 0; iNode < nbNodes; iNode++ )
8167           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8168                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8169               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8170           {
8171             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8172             curElemList.push_back( e );
8173           }
8174       }
8175     }
8176     // analyse the found
8177
8178     int nbNewBorders = curElemList.size();
8179     if ( nbNewBorders == 0 ) {
8180       // no free border furthermore
8181       return !needTheLast;
8182     }
8183     else if ( nbNewBorders == 1 ) {
8184       // one more element found
8185       nIgnore = nStart;
8186       nStart = nStartList.front();
8187       curElem = curElemList.front();
8188       theFaces.push_back( curElem );
8189       theNodes.push_back( nStart );
8190     }
8191     else {
8192       // several continuations found
8193       list< const SMDS_MeshElement* >::iterator curElemIt;
8194       list< const SMDS_MeshNode* >::iterator nStartIt;
8195       // check if one of them reached the last node
8196       if ( needTheLast ) {
8197         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8198              curElemIt!= curElemList.end();
8199              curElemIt++, nStartIt++ )
8200           if ( *nStartIt == theLastNode ) {
8201             theFaces.push_back( *curElemIt );
8202             theNodes.push_back( *nStartIt );
8203             return true;
8204           }
8205       }
8206       // find the best free border by the continuations
8207       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8208       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8209       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8210            curElemIt!= curElemList.end();
8211            curElemIt++, nStartIt++ )
8212       {
8213         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8214         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8215         // find one more free border
8216         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8217           cNL->clear();
8218           cFL->clear();
8219         }
8220         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8221           // choice: clear a worse one
8222           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8223           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8224           contNodes[ iWorse ].clear();
8225           contFaces[ iWorse ].clear();
8226         }
8227       }
8228       if ( contNodes[0].empty() && contNodes[1].empty() )
8229         return false;
8230
8231       // append the best free border
8232       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8233       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8234       theNodes.pop_back(); // remove nIgnore
8235       theNodes.pop_back(); // remove nStart
8236       theFaces.pop_back(); // remove curElem
8237       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8238       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8239       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8240       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8241       return true;
8242
8243     } // several continuations found
8244   } // while ( nStart != theLastNode )
8245
8246   return true;
8247 }
8248
8249 //=======================================================================
8250 //function : CheckFreeBorderNodes
8251 //purpose  : Return true if the tree nodes are on a free border
8252 //=======================================================================
8253
8254 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8255                                             const SMDS_MeshNode* theNode2,
8256                                             const SMDS_MeshNode* theNode3)
8257 {
8258   list< const SMDS_MeshNode* > nodes;
8259   list< const SMDS_MeshElement* > faces;
8260   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8261 }
8262
8263 //=======================================================================
8264 //function : SewFreeBorder
8265 //purpose  :
8266 //warning  : for border-to-side sewing theSideSecondNode is considered as
8267 //           the last side node and theSideThirdNode is not used
8268 //=======================================================================
8269
8270 SMESH_MeshEditor::Sew_Error
8271 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8272                                  const SMDS_MeshNode* theBordSecondNode,
8273                                  const SMDS_MeshNode* theBordLastNode,
8274                                  const SMDS_MeshNode* theSideFirstNode,
8275                                  const SMDS_MeshNode* theSideSecondNode,
8276                                  const SMDS_MeshNode* theSideThirdNode,
8277                                  const bool           theSideIsFreeBorder,
8278                                  const bool           toCreatePolygons,
8279                                  const bool           toCreatePolyedrs)
8280 {
8281   myLastCreatedElems.Clear();
8282   myLastCreatedNodes.Clear();
8283
8284   Sew_Error aResult = SEW_OK;
8285
8286   // ====================================
8287   //    find side nodes and elements
8288   // ====================================
8289
8290   list< const SMDS_MeshNode* >    nSide[ 2 ];
8291   list< const SMDS_MeshElement* > eSide[ 2 ];
8292   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8293   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8294
8295   // Free border 1
8296   // --------------
8297   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8298                       nSide[0], eSide[0])) {
8299     MESSAGE(" Free Border 1 not found " );
8300     aResult = SEW_BORDER1_NOT_FOUND;
8301   }
8302   if (theSideIsFreeBorder) {
8303     // Free border 2
8304     // --------------
8305     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8306                         nSide[1], eSide[1])) {
8307       MESSAGE(" Free Border 2 not found " );
8308       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8309     }
8310   }
8311   if ( aResult != SEW_OK )
8312     return aResult;
8313
8314   if (!theSideIsFreeBorder) {
8315     // Side 2
8316     // --------------
8317
8318     // -------------------------------------------------------------------------
8319     // Algo:
8320     // 1. If nodes to merge are not coincident, move nodes of the free border
8321     //    from the coord sys defined by the direction from the first to last
8322     //    nodes of the border to the correspondent sys of the side 2
8323     // 2. On the side 2, find the links most co-directed with the correspondent
8324     //    links of the free border
8325     // -------------------------------------------------------------------------
8326
8327     // 1. Since sewing may break if there are volumes to split on the side 2,
8328     //    we won't move nodes but just compute new coordinates for them
8329     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8330     TNodeXYZMap nBordXYZ;
8331     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8332     list< const SMDS_MeshNode* >::iterator nBordIt;
8333
8334     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8335     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8336     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8337     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8338     double tol2 = 1.e-8;
8339     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8340     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8341       // Need node movement.
8342
8343       // find X and Z axes to create trsf
8344       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8345       gp_Vec X = Zs ^ Zb;
8346       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8347         // Zb || Zs
8348         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8349
8350       // coord systems
8351       gp_Ax3 toBordAx( Pb1, Zb, X );
8352       gp_Ax3 fromSideAx( Ps1, Zs, X );
8353       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8354       // set trsf
8355       gp_Trsf toBordSys, fromSide2Sys;
8356       toBordSys.SetTransformation( toBordAx );
8357       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8358       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8359
8360       // move
8361       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8362         const SMDS_MeshNode* n = *nBordIt;
8363         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8364         toBordSys.Transforms( xyz );
8365         fromSide2Sys.Transforms( xyz );
8366         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8367       }
8368     }
8369     else {
8370       // just insert nodes XYZ in the nBordXYZ map
8371       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8372         const SMDS_MeshNode* n = *nBordIt;
8373         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8374       }
8375     }
8376
8377     // 2. On the side 2, find the links most co-directed with the correspondent
8378     //    links of the free border
8379
8380     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8381     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8382     sideNodes.push_back( theSideFirstNode );
8383
8384     bool hasVolumes = false;
8385     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8386     set<long> foundSideLinkIDs, checkedLinkIDs;
8387     SMDS_VolumeTool volume;
8388     //const SMDS_MeshNode* faceNodes[ 4 ];
8389
8390     const SMDS_MeshNode*    sideNode;
8391     const SMDS_MeshElement* sideElem  = 0;
8392     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8393     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8394     nBordIt = bordNodes.begin();
8395     nBordIt++;
8396     // border node position and border link direction to compare with
8397     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8398     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8399     // choose next side node by link direction or by closeness to
8400     // the current border node:
8401     bool searchByDir = ( *nBordIt != theBordLastNode );
8402     do {
8403       // find the next node on the Side 2
8404       sideNode = 0;
8405       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8406       long linkID;
8407       checkedLinkIDs.clear();
8408       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8409
8410       // loop on inverse elements of current node (prevSideNode) on the Side 2
8411       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8412       while ( invElemIt->more() )
8413       {
8414         const SMDS_MeshElement* elem = invElemIt->next();
8415         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8416         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8417         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8418         bool isVolume = volume.Set( elem );
8419         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8420         if ( isVolume ) // --volume
8421           hasVolumes = true;
8422         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8423           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8424           if(elem->IsQuadratic()) {
8425             const SMDS_VtkFace* F =
8426               dynamic_cast<const SMDS_VtkFace*>(elem);
8427             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8428             // use special nodes iterator
8429             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8430             while( anIter->more() ) {
8431               nodes[ iNode ] = cast2Node(anIter->next());
8432               if ( nodes[ iNode++ ] == prevSideNode )
8433                 iPrevNode = iNode - 1;
8434             }
8435           }
8436           else {
8437             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8438             while ( nIt->more() ) {
8439               nodes[ iNode ] = cast2Node( nIt->next() );
8440               if ( nodes[ iNode++ ] == prevSideNode )
8441                 iPrevNode = iNode - 1;
8442             }
8443           }
8444           // there are 2 links to check
8445           nbNodes = 2;
8446         }
8447         else // --edge
8448           continue;
8449         // loop on links, to be precise, on the second node of links
8450         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8451           const SMDS_MeshNode* n = nodes[ iNode ];
8452           if ( isVolume ) {
8453             if ( !volume.IsLinked( n, prevSideNode ))
8454               continue;
8455           }
8456           else {
8457             if ( iNode ) // a node before prevSideNode
8458               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8459             else         // a node after prevSideNode
8460               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8461           }
8462           // check if this link was already used
8463           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8464           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8465           if (!isJustChecked &&
8466               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8467           {
8468             // test a link geometrically
8469             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8470             bool linkIsBetter = false;
8471             double dot = 0.0, dist = 0.0;
8472             if ( searchByDir ) { // choose most co-directed link
8473               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8474               linkIsBetter = ( dot > maxDot );
8475             }
8476             else { // choose link with the node closest to bordPos
8477               dist = ( nextXYZ - bordPos ).SquareModulus();
8478               linkIsBetter = ( dist < minDist );
8479             }
8480             if ( linkIsBetter ) {
8481               maxDot = dot;
8482               minDist = dist;
8483               linkID = iLink;
8484               sideNode = n;
8485               sideElem = elem;
8486             }
8487           }
8488         }
8489       } // loop on inverse elements of prevSideNode
8490
8491       if ( !sideNode ) {
8492         MESSAGE(" Can't find path by links of the Side 2 ");
8493         return SEW_BAD_SIDE_NODES;
8494       }
8495       sideNodes.push_back( sideNode );
8496       sideElems.push_back( sideElem );
8497       foundSideLinkIDs.insert ( linkID );
8498       prevSideNode = sideNode;
8499
8500       if ( *nBordIt == theBordLastNode )
8501         searchByDir = false;
8502       else {
8503         // find the next border link to compare with
8504         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8505         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8506         // move to next border node if sideNode is before forward border node (bordPos)
8507         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8508           prevBordNode = *nBordIt;
8509           nBordIt++;
8510           bordPos = nBordXYZ[ *nBordIt ];
8511           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8512           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8513         }
8514       }
8515     }
8516     while ( sideNode != theSideSecondNode );
8517
8518     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8519       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8520       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8521     }
8522   } // end nodes search on the side 2
8523
8524   // ============================
8525   // sew the border to the side 2
8526   // ============================
8527
8528   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8529   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8530
8531   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8532   if ( toMergeConformal && toCreatePolygons )
8533   {
8534     // do not merge quadrangles if polygons are OK (IPAL0052824)
8535     eIt[0] = eSide[0].begin();
8536     eIt[1] = eSide[1].begin();
8537     bool allQuads[2] = { true, true };
8538     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8539       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8540         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8541     }
8542     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8543   }
8544
8545   TListOfListOfNodes nodeGroupsToMerge;
8546   if (( toMergeConformal ) ||
8547       ( theSideIsFreeBorder && !theSideThirdNode )) {
8548
8549     // all nodes are to be merged
8550
8551     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8552          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8553          nIt[0]++, nIt[1]++ )
8554     {
8555       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8556       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8557       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8558     }
8559   }
8560   else {
8561
8562     // insert new nodes into the border and the side to get equal nb of segments
8563
8564     // get normalized parameters of nodes on the borders
8565     vector< double > param[ 2 ];
8566     param[0].resize( maxNbNodes );
8567     param[1].resize( maxNbNodes );
8568     int iNode, iBord;
8569     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8570       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8571       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8572       const SMDS_MeshNode* nPrev = *nIt;
8573       double bordLength = 0;
8574       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8575         const SMDS_MeshNode* nCur = *nIt;
8576         gp_XYZ segment (nCur->X() - nPrev->X(),
8577                         nCur->Y() - nPrev->Y(),
8578                         nCur->Z() - nPrev->Z());
8579         double segmentLen = segment.Modulus();
8580         bordLength += segmentLen;
8581         param[ iBord ][ iNode ] = bordLength;
8582         nPrev = nCur;
8583       }
8584       // normalize within [0,1]
8585       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8586         param[ iBord ][ iNode ] /= bordLength;
8587       }
8588     }
8589
8590     // loop on border segments
8591     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8592     int i[ 2 ] = { 0, 0 };
8593     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8594     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8595
8596     TElemOfNodeListMap insertMap;
8597     TElemOfNodeListMap::iterator insertMapIt;
8598     // insertMap is
8599     // key:   elem to insert nodes into
8600     // value: 2 nodes to insert between + nodes to be inserted
8601     do {
8602       bool next[ 2 ] = { false, false };
8603
8604       // find min adjacent segment length after sewing
8605       double nextParam = 10., prevParam = 0;
8606       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8607         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8608           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8609         if ( i[ iBord ] > 0 )
8610           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8611       }
8612       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8613       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8614       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8615
8616       // choose to insert or to merge nodes
8617       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8618       if ( Abs( du ) <= minSegLen * 0.2 ) {
8619         // merge
8620         // ------
8621         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8622         const SMDS_MeshNode* n0 = *nIt[0];
8623         const SMDS_MeshNode* n1 = *nIt[1];
8624         nodeGroupsToMerge.back().push_back( n1 );
8625         nodeGroupsToMerge.back().push_back( n0 );
8626         // position of node of the border changes due to merge
8627         param[ 0 ][ i[0] ] += du;
8628         // move n1 for the sake of elem shape evaluation during insertion.
8629         // n1 will be removed by MergeNodes() anyway
8630         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8631         next[0] = next[1] = true;
8632       }
8633       else {
8634         // insert
8635         // ------
8636         int intoBord = ( du < 0 ) ? 0 : 1;
8637         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8638         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8639         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8640         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8641         if ( intoBord == 1 ) {
8642           // move node of the border to be on a link of elem of the side
8643           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8644           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8645           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8646           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8647           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8648         }
8649         insertMapIt = insertMap.find( elem );
8650         bool  notFound = ( insertMapIt == insertMap.end() );
8651         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8652         if ( otherLink ) {
8653           // insert into another link of the same element:
8654           // 1. perform insertion into the other link of the elem
8655           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8656           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8657           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8658           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8659           // 2. perform insertion into the link of adjacent faces
8660           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8661             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8662           }
8663           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8664             InsertNodesIntoLink( seg, n12, n22, nodeList );
8665           }
8666           if (toCreatePolyedrs) {
8667             // perform insertion into the links of adjacent volumes
8668             UpdateVolumes(n12, n22, nodeList);
8669           }
8670           // 3. find an element appeared on n1 and n2 after the insertion
8671           insertMap.erase( elem );
8672           elem = findAdjacentFace( n1, n2, 0 );
8673         }
8674         if ( notFound || otherLink ) {
8675           // add element and nodes of the side into the insertMap
8676           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8677           (*insertMapIt).second.push_back( n1 );
8678           (*insertMapIt).second.push_back( n2 );
8679         }
8680         // add node to be inserted into elem
8681         (*insertMapIt).second.push_back( nIns );
8682         next[ 1 - intoBord ] = true;
8683       }
8684
8685       // go to the next segment
8686       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8687         if ( next[ iBord ] ) {
8688           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8689             eIt[ iBord ]++;
8690           nPrev[ iBord ] = *nIt[ iBord ];
8691           nIt[ iBord ]++; i[ iBord ]++;
8692         }
8693       }
8694     }
8695     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8696
8697     // perform insertion of nodes into elements
8698
8699     for (insertMapIt = insertMap.begin();
8700          insertMapIt != insertMap.end();
8701          insertMapIt++ )
8702     {
8703       const SMDS_MeshElement* elem = (*insertMapIt).first;
8704       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8705       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8706       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8707
8708       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8709
8710       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8711         InsertNodesIntoLink( seg, n1, n2, nodeList );
8712       }
8713
8714       if ( !theSideIsFreeBorder ) {
8715         // look for and insert nodes into the faces adjacent to elem
8716         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8717           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8718         }
8719       }
8720       if (toCreatePolyedrs) {
8721         // perform insertion into the links of adjacent volumes
8722         UpdateVolumes(n1, n2, nodeList);
8723       }
8724     }
8725   } // end: insert new nodes
8726
8727   MergeNodes ( nodeGroupsToMerge );
8728
8729
8730   // Remove coincident segments
8731
8732   // get new segments
8733   TIDSortedElemSet segments;
8734   SMESH_SequenceOfElemPtr newFaces;
8735   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8736   {
8737     if ( !myLastCreatedElems(i) ) continue;
8738     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8739       segments.insert( segments.end(), myLastCreatedElems(i) );
8740     else
8741       newFaces.Append( myLastCreatedElems(i) );
8742   }
8743   // get segments adjacent to merged nodes
8744   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8745   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8746   {
8747     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8748     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8749     while ( segIt->more() )
8750       segments.insert( segIt->next() );
8751   }
8752
8753   // find coincident
8754   TListOfListOfElementsID equalGroups;
8755   if ( !segments.empty() )
8756     FindEqualElements( segments, equalGroups );
8757   if ( !equalGroups.empty() )
8758   {
8759     // remove from segments those that will be removed
8760     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8761     for ( ; itGroups != equalGroups.end(); ++itGroups )
8762     {
8763       list< int >& group = *itGroups;
8764       list< int >::iterator id = group.begin();
8765       for ( ++id; id != group.end(); ++id )
8766         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8767           segments.erase( seg );
8768     }
8769     // remove equal segments
8770     MergeElements( equalGroups );
8771
8772     // restore myLastCreatedElems
8773     myLastCreatedElems = newFaces;
8774     TIDSortedElemSet::iterator seg = segments.begin();
8775     for ( ; seg != segments.end(); ++seg )
8776       myLastCreatedElems.Append( *seg );
8777   }
8778
8779   return aResult;
8780 }
8781
8782 //=======================================================================
8783 //function : InsertNodesIntoLink
8784 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8785 //           and theBetweenNode2 and split theElement
8786 //=======================================================================
8787
8788 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8789                                            const SMDS_MeshNode*        theBetweenNode1,
8790                                            const SMDS_MeshNode*        theBetweenNode2,
8791                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8792                                            const bool                  toCreatePoly)
8793 {
8794   if ( !theElement ) return;
8795
8796   SMESHDS_Mesh *aMesh = GetMeshDS();
8797   vector<const SMDS_MeshElement*> newElems;
8798
8799   if ( theElement->GetType() == SMDSAbs_Edge )
8800   {
8801     theNodesToInsert.push_front( theBetweenNode1 );
8802     theNodesToInsert.push_back ( theBetweenNode2 );
8803     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8804     const SMDS_MeshNode* n1 = *n;
8805     for ( ++n; n != theNodesToInsert.end(); ++n )
8806     {
8807       const SMDS_MeshNode* n2 = *n;
8808       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8809         AddToSameGroups( seg, theElement, aMesh );
8810       else
8811         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8812       n1 = n2;
8813     }
8814     theNodesToInsert.pop_front();
8815     theNodesToInsert.pop_back();
8816
8817     if ( theElement->IsQuadratic() ) // add a not split part
8818     {
8819       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8820                                           theElement->end_nodes() );
8821       int iOther = 0, nbN = nodes.size();
8822       for ( ; iOther < nbN; ++iOther )
8823         if ( nodes[iOther] != theBetweenNode1 &&
8824              nodes[iOther] != theBetweenNode2 )
8825           break;
8826       if      ( iOther == 0 )
8827       {
8828         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8829           AddToSameGroups( seg, theElement, aMesh );
8830         else
8831           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8832       }
8833       else if ( iOther == 2 )
8834       {
8835         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8836           AddToSameGroups( seg, theElement, aMesh );
8837         else
8838           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8839       }
8840     }
8841     // treat new elements
8842     for ( size_t i = 0; i < newElems.size(); ++i )
8843       if ( newElems[i] )
8844       {
8845         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8846         myLastCreatedElems.Append( newElems[i] );
8847       }
8848     ReplaceElemInGroups( theElement, newElems, aMesh );
8849     aMesh->RemoveElement( theElement );
8850     return;
8851
8852   } // if ( theElement->GetType() == SMDSAbs_Edge )
8853
8854   const SMDS_MeshElement* theFace = theElement;
8855   if ( theFace->GetType() != SMDSAbs_Face ) return;
8856
8857   // find indices of 2 link nodes and of the rest nodes
8858   int iNode = 0, il1, il2, i3, i4;
8859   il1 = il2 = i3 = i4 = -1;
8860   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8861
8862   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8863   while ( nodeIt->more() ) {
8864     const SMDS_MeshNode* n = nodeIt->next();
8865     if ( n == theBetweenNode1 )
8866       il1 = iNode;
8867     else if ( n == theBetweenNode2 )
8868       il2 = iNode;
8869     else if ( i3 < 0 )
8870       i3 = iNode;
8871     else
8872       i4 = iNode;
8873     nodes[ iNode++ ] = n;
8874   }
8875   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8876     return ;
8877
8878   // arrange link nodes to go one after another regarding the face orientation
8879   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8880   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8881   if ( reverse ) {
8882     iNode = il1;
8883     il1 = il2;
8884     il2 = iNode;
8885     aNodesToInsert.reverse();
8886   }
8887   // check that not link nodes of a quadrangles are in good order
8888   int nbFaceNodes = theFace->NbNodes();
8889   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8890     iNode = i3;
8891     i3 = i4;
8892     i4 = iNode;
8893   }
8894
8895   if (toCreatePoly || theFace->IsPoly()) {
8896
8897     iNode = 0;
8898     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8899
8900     // add nodes of face up to first node of link
8901     bool isFLN = false;
8902
8903     if ( theFace->IsQuadratic() ) {
8904       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8905       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8906       // use special nodes iterator
8907       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8908       while( anIter->more()  && !isFLN ) {
8909         const SMDS_MeshNode* n = cast2Node(anIter->next());
8910         poly_nodes[iNode++] = n;
8911         if (n == nodes[il1]) {
8912           isFLN = true;
8913         }
8914       }
8915       // add nodes to insert
8916       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8917       for (; nIt != aNodesToInsert.end(); nIt++) {
8918         poly_nodes[iNode++] = *nIt;
8919       }
8920       // add nodes of face starting from last node of link
8921       while ( anIter->more() ) {
8922         poly_nodes[iNode++] = cast2Node(anIter->next());
8923       }
8924     }
8925     else {
8926       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8927       while ( nodeIt->more() && !isFLN ) {
8928         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8929         poly_nodes[iNode++] = n;
8930         if (n == nodes[il1]) {
8931           isFLN = true;
8932         }
8933       }
8934       // add nodes to insert
8935       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8936       for (; nIt != aNodesToInsert.end(); nIt++) {
8937         poly_nodes[iNode++] = *nIt;
8938       }
8939       // add nodes of face starting from last node of link
8940       while ( nodeIt->more() ) {
8941         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8942         poly_nodes[iNode++] = n;
8943       }
8944     }
8945
8946     // make a new face
8947     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8948   }
8949
8950   else if ( !theFace->IsQuadratic() )
8951   {
8952     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8953     int nbLinkNodes = 2 + aNodesToInsert.size();
8954     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8955     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8956     linkNodes[ 0 ] = nodes[ il1 ];
8957     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8958     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8959     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8960       linkNodes[ iNode++ ] = *nIt;
8961     }
8962     // decide how to split a quadrangle: compare possible variants
8963     // and choose which of splits to be a quadrangle
8964     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8965     if ( nbFaceNodes == 3 ) {
8966       iBestQuad = nbSplits;
8967       i4 = i3;
8968     }
8969     else if ( nbFaceNodes == 4 ) {
8970       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8971       double aBestRate = DBL_MAX;
8972       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8973         i1 = 0; i2 = 1;
8974         double aBadRate = 0;
8975         // evaluate elements quality
8976         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8977           if ( iSplit == iQuad ) {
8978             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8979                                    linkNodes[ i2++ ],
8980                                    nodes[ i3 ],
8981                                    nodes[ i4 ]);
8982             aBadRate += getBadRate( &quad, aCrit );
8983           }
8984           else {
8985             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8986                                    linkNodes[ i2++ ],
8987                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8988             aBadRate += getBadRate( &tria, aCrit );
8989           }
8990         }
8991         // choice
8992         if ( aBadRate < aBestRate ) {
8993           iBestQuad = iQuad;
8994           aBestRate = aBadRate;
8995         }
8996       }
8997     }
8998
8999     // create new elements
9000     i1 = 0; i2 = 1;
9001     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9002     {
9003       if ( iSplit == iBestQuad )
9004         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9005                                             linkNodes[ i2++ ],
9006                                             nodes[ i3 ],
9007                                             nodes[ i4 ]));
9008       else
9009         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9010                                             linkNodes[ i2++ ],
9011                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9012     }
9013
9014     const SMDS_MeshNode* newNodes[ 4 ];
9015     newNodes[ 0 ] = linkNodes[ i1 ];
9016     newNodes[ 1 ] = linkNodes[ i2 ];
9017     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9018     newNodes[ 3 ] = nodes[ i4 ];
9019     if (iSplit == iBestQuad)
9020       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9021     else
9022       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9023
9024   } // end if(!theFace->IsQuadratic())
9025
9026   else { // theFace is quadratic
9027     // we have to split theFace on simple triangles and one simple quadrangle
9028     int tmp = il1/2;
9029     int nbshift = tmp*2;
9030     // shift nodes in nodes[] by nbshift
9031     int i,j;
9032     for(i=0; i<nbshift; i++) {
9033       const SMDS_MeshNode* n = nodes[0];
9034       for(j=0; j<nbFaceNodes-1; j++) {
9035         nodes[j] = nodes[j+1];
9036       }
9037       nodes[nbFaceNodes-1] = n;
9038     }
9039     il1 = il1 - nbshift;
9040     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9041     //   n0      n1     n2    n0      n1     n2
9042     //     +-----+-----+        +-----+-----+
9043     //      \         /         |           |
9044     //       \       /          |           |
9045     //      n5+     +n3       n7+           +n3
9046     //         \   /            |           |
9047     //          \ /             |           |
9048     //           +              +-----+-----+
9049     //           n4           n6      n5     n4
9050
9051     // create new elements
9052     int n1,n2,n3;
9053     if ( nbFaceNodes == 6 ) { // quadratic triangle
9054       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9055       if ( theFace->IsMediumNode(nodes[il1]) ) {
9056         // create quadrangle
9057         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9058         n1 = 1;
9059         n2 = 2;
9060         n3 = 3;
9061       }
9062       else {
9063         // create quadrangle
9064         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9065         n1 = 0;
9066         n2 = 1;
9067         n3 = 5;
9068       }
9069     }
9070     else { // nbFaceNodes==8 - quadratic quadrangle
9071       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9072       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9073       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9074       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9075         // create quadrangle
9076         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9077         n1 = 1;
9078         n2 = 2;
9079         n3 = 3;
9080       }
9081       else {
9082         // create quadrangle
9083         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9084         n1 = 0;
9085         n2 = 1;
9086         n3 = 7;
9087       }
9088     }
9089     // create needed triangles using n1,n2,n3 and inserted nodes
9090     int nbn = 2 + aNodesToInsert.size();
9091     vector<const SMDS_MeshNode*> aNodes(nbn);
9092     aNodes[0    ] = nodes[n1];
9093     aNodes[nbn-1] = nodes[n2];
9094     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9095     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9096       aNodes[iNode++] = *nIt;
9097     }
9098     for ( i = 1; i < nbn; i++ )
9099       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9100   }
9101
9102   // remove the old face
9103   for ( size_t i = 0; i < newElems.size(); ++i )
9104     if ( newElems[i] )
9105     {
9106       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9107       myLastCreatedElems.Append( newElems[i] );
9108     }
9109   ReplaceElemInGroups( theFace, newElems, aMesh );
9110   aMesh->RemoveElement(theFace);
9111
9112 } // InsertNodesIntoLink()
9113
9114 //=======================================================================
9115 //function : UpdateVolumes
9116 //purpose  :
9117 //=======================================================================
9118
9119 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9120                                       const SMDS_MeshNode*        theBetweenNode2,
9121                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9122 {
9123   myLastCreatedElems.Clear();
9124   myLastCreatedNodes.Clear();
9125
9126   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9127   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9128     const SMDS_MeshElement* elem = invElemIt->next();
9129
9130     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9131     SMDS_VolumeTool aVolume (elem);
9132     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9133       continue;
9134
9135     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9136     int iface, nbFaces = aVolume.NbFaces();
9137     vector<const SMDS_MeshNode *> poly_nodes;
9138     vector<int> quantities (nbFaces);
9139
9140     for (iface = 0; iface < nbFaces; iface++) {
9141       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9142       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9143       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9144
9145       for (int inode = 0; inode < nbFaceNodes; inode++) {
9146         poly_nodes.push_back(faceNodes[inode]);
9147
9148         if (nbInserted == 0) {
9149           if (faceNodes[inode] == theBetweenNode1) {
9150             if (faceNodes[inode + 1] == theBetweenNode2) {
9151               nbInserted = theNodesToInsert.size();
9152
9153               // add nodes to insert
9154               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9155               for (; nIt != theNodesToInsert.end(); nIt++) {
9156                 poly_nodes.push_back(*nIt);
9157               }
9158             }
9159           }
9160           else if (faceNodes[inode] == theBetweenNode2) {
9161             if (faceNodes[inode + 1] == theBetweenNode1) {
9162               nbInserted = theNodesToInsert.size();
9163
9164               // add nodes to insert in reversed order
9165               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9166               nIt--;
9167               for (; nIt != theNodesToInsert.begin(); nIt--) {
9168                 poly_nodes.push_back(*nIt);
9169               }
9170               poly_nodes.push_back(*nIt);
9171             }
9172           }
9173           else {
9174           }
9175         }
9176       }
9177       quantities[iface] = nbFaceNodes + nbInserted;
9178     }
9179
9180     // Replace the volume
9181     SMESHDS_Mesh *aMesh = GetMeshDS();
9182
9183     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9184     {
9185       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9186       myLastCreatedElems.Append( newElem );
9187       ReplaceElemInGroups( elem, newElem, aMesh );
9188     }
9189     aMesh->RemoveElement( elem );
9190   }
9191 }
9192
9193 namespace
9194 {
9195   //================================================================================
9196   /*!
9197    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9198    */
9199   //================================================================================
9200
9201   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9202                            vector<const SMDS_MeshNode *> & nodes,
9203                            vector<int> &                   nbNodeInFaces )
9204   {
9205     nodes.clear();
9206     nbNodeInFaces.clear();
9207     SMDS_VolumeTool vTool ( elem );
9208     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9209     {
9210       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9211       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9212       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9213     }
9214   }
9215 }
9216
9217 //=======================================================================
9218 /*!
9219  * \brief Convert elements contained in a sub-mesh to quadratic
9220  * \return int - nb of checked elements
9221  */
9222 //=======================================================================
9223
9224 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9225                                              SMESH_MesherHelper& theHelper,
9226                                              const bool          theForce3d)
9227 {
9228   //MESSAGE("convertElemToQuadratic");
9229   int nbElem = 0;
9230   if( !theSm ) return nbElem;
9231
9232   vector<int> nbNodeInFaces;
9233   vector<const SMDS_MeshNode *> nodes;
9234   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9235   while(ElemItr->more())
9236   {
9237     nbElem++;
9238     const SMDS_MeshElement* elem = ElemItr->next();
9239     if( !elem ) continue;
9240
9241     // analyse a necessity of conversion
9242     const SMDSAbs_ElementType aType = elem->GetType();
9243     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9244       continue;
9245     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9246     bool hasCentralNodes = false;
9247     if ( elem->IsQuadratic() )
9248     {
9249       bool alreadyOK;
9250       switch ( aGeomType ) {
9251       case SMDSEntity_Quad_Triangle:
9252       case SMDSEntity_Quad_Quadrangle:
9253       case SMDSEntity_Quad_Hexa:
9254       case SMDSEntity_Quad_Penta:
9255         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9256
9257       case SMDSEntity_BiQuad_Triangle:
9258       case SMDSEntity_BiQuad_Quadrangle:
9259       case SMDSEntity_TriQuad_Hexa:
9260       case SMDSEntity_BiQuad_Penta:
9261         alreadyOK = theHelper.GetIsBiQuadratic();
9262         hasCentralNodes = true;
9263         break;
9264       default:
9265         alreadyOK = true;
9266       }
9267       // take into account already present medium nodes
9268       switch ( aType ) {
9269       case SMDSAbs_Volume:
9270         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9271       case SMDSAbs_Face:
9272         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9273       case SMDSAbs_Edge:
9274         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9275       default:;
9276       }
9277       if ( alreadyOK )
9278         continue;
9279     }
9280     // get elem data needed to re-create it
9281     //
9282     const int id      = elem->GetID();
9283     const int nbNodes = elem->NbCornerNodes();
9284     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9285     if ( aGeomType == SMDSEntity_Polyhedra )
9286       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9287     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9288       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9289
9290     // remove a linear element
9291     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9292
9293     // remove central nodes of biquadratic elements (biquad->quad conversion)
9294     if ( hasCentralNodes )
9295       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9296         if ( nodes[i]->NbInverseElements() == 0 )
9297           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9298
9299     const SMDS_MeshElement* NewElem = 0;
9300
9301     switch( aType )
9302     {
9303     case SMDSAbs_Edge :
9304       {
9305         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9306         break;
9307       }
9308     case SMDSAbs_Face :
9309       {
9310         switch(nbNodes)
9311         {
9312         case 3:
9313           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9314           break;
9315         case 4:
9316           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9317           break;
9318         default:
9319           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9320         }
9321         break;
9322       }
9323     case SMDSAbs_Volume :
9324       {
9325         switch( aGeomType )
9326         {
9327         case SMDSEntity_Tetra:
9328           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9329           break;
9330         case SMDSEntity_Pyramid:
9331           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9332           break;
9333         case SMDSEntity_Penta:
9334         case SMDSEntity_Quad_Penta:
9335         case SMDSEntity_BiQuad_Penta:
9336           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9337           break;
9338         case SMDSEntity_Hexa:
9339         case SMDSEntity_Quad_Hexa:
9340         case SMDSEntity_TriQuad_Hexa:
9341           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9342                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9343           break;
9344         case SMDSEntity_Hexagonal_Prism:
9345         default:
9346           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9347         }
9348         break;
9349       }
9350     default :
9351       continue;
9352     }
9353     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9354     if( NewElem && NewElem->getshapeId() < 1 )
9355       theSm->AddElement( NewElem );
9356   }
9357   return nbElem;
9358 }
9359 //=======================================================================
9360 //function : ConvertToQuadratic
9361 //purpose  :
9362 //=======================================================================
9363
9364 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9365 {
9366   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9367   SMESHDS_Mesh* meshDS = GetMeshDS();
9368
9369   SMESH_MesherHelper aHelper(*myMesh);
9370
9371   aHelper.SetIsQuadratic( true );
9372   aHelper.SetIsBiQuadratic( theToBiQuad );
9373   aHelper.SetElementsOnShape(true);
9374   aHelper.ToFixNodeParameters( true );
9375
9376   // convert elements assigned to sub-meshes
9377   int nbCheckedElems = 0;
9378   if ( myMesh->HasShapeToMesh() )
9379   {
9380     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9381     {
9382       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9383       while ( smIt->more() ) {
9384         SMESH_subMesh* sm = smIt->next();
9385         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9386           aHelper.SetSubShape( sm->GetSubShape() );
9387           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9388         }
9389       }
9390     }
9391   }
9392
9393   // convert elements NOT assigned to sub-meshes
9394   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9395   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9396   {
9397     aHelper.SetElementsOnShape(false);
9398     SMESHDS_SubMesh *smDS = 0;
9399
9400     // convert edges
9401     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9402     while( aEdgeItr->more() )
9403     {
9404       const SMDS_MeshEdge* edge = aEdgeItr->next();
9405       if ( !edge->IsQuadratic() )
9406       {
9407         int                  id = edge->GetID();
9408         const SMDS_MeshNode* n1 = edge->GetNode(0);
9409         const SMDS_MeshNode* n2 = edge->GetNode(1);
9410
9411         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9412
9413         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9414         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9415       }
9416       else
9417       {
9418         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9419       }
9420     }
9421
9422     // convert faces
9423     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9424     while( aFaceItr->more() )
9425     {
9426       const SMDS_MeshFace* face = aFaceItr->next();
9427       if ( !face ) continue;
9428       
9429       const SMDSAbs_EntityType type = face->GetEntityType();
9430       bool alreadyOK;
9431       switch( type )
9432       {
9433       case SMDSEntity_Quad_Triangle:
9434       case SMDSEntity_Quad_Quadrangle:
9435         alreadyOK = !theToBiQuad;
9436         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9437         break;
9438       case SMDSEntity_BiQuad_Triangle:
9439       case SMDSEntity_BiQuad_Quadrangle:
9440         alreadyOK = theToBiQuad;
9441         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9442         break;
9443       default: alreadyOK = false;
9444       }
9445       if ( alreadyOK )
9446         continue;
9447
9448       const int id = face->GetID();
9449       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9450
9451       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9452
9453       SMDS_MeshFace * NewFace = 0;
9454       switch( type )
9455       {
9456       case SMDSEntity_Triangle:
9457       case SMDSEntity_Quad_Triangle:
9458       case SMDSEntity_BiQuad_Triangle:
9459         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9460         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9461           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9462         break;
9463
9464       case SMDSEntity_Quadrangle:
9465       case SMDSEntity_Quad_Quadrangle:
9466       case SMDSEntity_BiQuad_Quadrangle:
9467         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9468         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9469           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9470         break;
9471
9472       default:;
9473         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9474       }
9475       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9476     }
9477
9478     // convert volumes
9479     vector<int> nbNodeInFaces;
9480     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9481     while(aVolumeItr->more())
9482     {
9483       const SMDS_MeshVolume* volume = aVolumeItr->next();
9484       if ( !volume ) continue;
9485
9486       const SMDSAbs_EntityType type = volume->GetEntityType();
9487       if ( volume->IsQuadratic() )
9488       {
9489         bool alreadyOK;
9490         switch ( type )
9491         {
9492         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9493         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9494         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9495         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9496         default:                      alreadyOK = true;
9497         }
9498         if ( alreadyOK )
9499         {
9500           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9501           continue;
9502         }
9503       }
9504       const int id = volume->GetID();
9505       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9506       if ( type == SMDSEntity_Polyhedra )
9507         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9508       else if ( type == SMDSEntity_Hexagonal_Prism )
9509         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9510
9511       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9512
9513       SMDS_MeshVolume * NewVolume = 0;
9514       switch ( type )
9515       {
9516       case SMDSEntity_Tetra:
9517         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9518         break;
9519       case SMDSEntity_Hexa:
9520       case SMDSEntity_Quad_Hexa:
9521       case SMDSEntity_TriQuad_Hexa:
9522         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9523                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9524         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9525           if ( nodes[i]->NbInverseElements() == 0 )
9526             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9527         break;
9528       case SMDSEntity_Pyramid:
9529         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9530                                       nodes[3], nodes[4], id, theForce3d);
9531         break;
9532       case SMDSEntity_Penta:
9533       case SMDSEntity_Quad_Penta:
9534       case SMDSEntity_BiQuad_Penta:
9535         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9536                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9537         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9538           if ( nodes[i]->NbInverseElements() == 0 )
9539             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9540         break;
9541       case SMDSEntity_Hexagonal_Prism:
9542       default:
9543         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9544       }
9545       ReplaceElemInGroups(volume, NewVolume, meshDS);
9546     }
9547   }
9548
9549   if ( !theForce3d )
9550   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9551     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9552     // aHelper.FixQuadraticElements(myError);
9553     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9554   }
9555 }
9556
9557 //================================================================================
9558 /*!
9559  * \brief Makes given elements quadratic
9560  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9561  *  \param theElements - elements to make quadratic
9562  */
9563 //================================================================================
9564
9565 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9566                                           TIDSortedElemSet& theElements,
9567                                           const bool        theToBiQuad)
9568 {
9569   if ( theElements.empty() ) return;
9570
9571   // we believe that all theElements are of the same type
9572   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9573
9574   // get all nodes shared by theElements
9575   TIDSortedNodeSet allNodes;
9576   TIDSortedElemSet::iterator eIt = theElements.begin();
9577   for ( ; eIt != theElements.end(); ++eIt )
9578     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9579
9580   // complete theElements with elements of lower dim whose all nodes are in allNodes
9581
9582   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9583   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9584   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9585   for ( ; nIt != allNodes.end(); ++nIt )
9586   {
9587     const SMDS_MeshNode* n = *nIt;
9588     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9589     while ( invIt->more() )
9590     {
9591       const SMDS_MeshElement*      e = invIt->next();
9592       const SMDSAbs_ElementType type = e->GetType();
9593       if ( e->IsQuadratic() )
9594       {
9595         quadAdjacentElems[ type ].insert( e );
9596
9597         bool alreadyOK;
9598         switch ( e->GetEntityType() ) {
9599         case SMDSEntity_Quad_Triangle:
9600         case SMDSEntity_Quad_Quadrangle:
9601         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9602         case SMDSEntity_BiQuad_Triangle:
9603         case SMDSEntity_BiQuad_Quadrangle:
9604         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9605         default:                           alreadyOK = true;
9606         }
9607         if ( alreadyOK )
9608           continue;
9609       }
9610       if ( type >= elemType )
9611         continue; // same type or more complex linear element
9612
9613       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9614         continue; // e is already checked
9615
9616       // check nodes
9617       bool allIn = true;
9618       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9619       while ( nodeIt->more() && allIn )
9620         allIn = allNodes.count( nodeIt->next() );
9621       if ( allIn )
9622         theElements.insert(e );
9623     }
9624   }
9625
9626   SMESH_MesherHelper helper(*myMesh);
9627   helper.SetIsQuadratic( true );
9628   helper.SetIsBiQuadratic( theToBiQuad );
9629
9630   // add links of quadratic adjacent elements to the helper
9631
9632   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9633     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9634           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9635     {
9636       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9637     }
9638   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9639     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9640           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9641     {
9642       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9643     }
9644   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9645     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9646           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9647     {
9648       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9649     }
9650
9651   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9652
9653   SMESHDS_Mesh*  meshDS = GetMeshDS();
9654   SMESHDS_SubMesh* smDS = 0;
9655   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9656   {
9657     const SMDS_MeshElement* elem = *eIt;
9658
9659     bool alreadyOK;
9660     int nbCentralNodes = 0;
9661     switch ( elem->GetEntityType() ) {
9662       // linear convertible
9663     case SMDSEntity_Edge:
9664     case SMDSEntity_Triangle:
9665     case SMDSEntity_Quadrangle:
9666     case SMDSEntity_Tetra:
9667     case SMDSEntity_Pyramid:
9668     case SMDSEntity_Hexa:
9669     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9670       // quadratic that can become bi-quadratic
9671     case SMDSEntity_Quad_Triangle:
9672     case SMDSEntity_Quad_Quadrangle:
9673     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9674       // bi-quadratic
9675     case SMDSEntity_BiQuad_Triangle:
9676     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9677     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9678       // the rest
9679     default:                           alreadyOK = true;
9680     }
9681     if ( alreadyOK ) continue;
9682
9683     const SMDSAbs_ElementType type = elem->GetType();
9684     const int                   id = elem->GetID();
9685     const int              nbNodes = elem->NbCornerNodes();
9686     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9687
9688     helper.SetSubShape( elem->getshapeId() );
9689
9690     if ( !smDS || !smDS->Contains( elem ))
9691       smDS = meshDS->MeshElements( elem->getshapeId() );
9692     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9693
9694     SMDS_MeshElement * newElem = 0;
9695     switch( nbNodes )
9696     {
9697     case 4: // cases for most frequently used element types go first (for optimization)
9698       if ( type == SMDSAbs_Volume )
9699         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9700       else
9701         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9702       break;
9703     case 8:
9704       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9705                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9706       break;
9707     case 3:
9708       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9709       break;
9710     case 2:
9711       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9712       break;
9713     case 5:
9714       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9715                                  nodes[4], id, theForce3d);
9716       break;
9717     case 6:
9718       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9719                                  nodes[4], nodes[5], id, theForce3d);
9720       break;
9721     default:;
9722     }
9723     ReplaceElemInGroups( elem, newElem, meshDS);
9724     if( newElem && smDS )
9725       smDS->AddElement( newElem );
9726
9727      // remove central nodes
9728     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9729       if ( nodes[i]->NbInverseElements() == 0 )
9730         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9731
9732   } // loop on theElements
9733
9734   if ( !theForce3d )
9735   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9736     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9737     // helper.FixQuadraticElements( myError );
9738     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9739   }
9740 }
9741
9742 //=======================================================================
9743 /*!
9744  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9745  * \return int - nb of checked elements
9746  */
9747 //=======================================================================
9748
9749 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9750                                      SMDS_ElemIteratorPtr theItr,
9751                                      const int            theShapeID)
9752 {
9753   int nbElem = 0;
9754   SMESHDS_Mesh* meshDS = GetMeshDS();
9755   ElemFeatures elemType;
9756   vector<const SMDS_MeshNode *> nodes;
9757
9758   while( theItr->more() )
9759   {
9760     const SMDS_MeshElement* elem = theItr->next();
9761     nbElem++;
9762     if( elem && elem->IsQuadratic())
9763     {
9764       // get elem data
9765       int nbCornerNodes = elem->NbCornerNodes();
9766       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9767
9768       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9769
9770       //remove a quadratic element
9771       if ( !theSm || !theSm->Contains( elem ))
9772         theSm = meshDS->MeshElements( elem->getshapeId() );
9773       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9774
9775       // remove medium nodes
9776       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9777         if ( nodes[i]->NbInverseElements() == 0 )
9778           meshDS->RemoveFreeNode( nodes[i], theSm );
9779
9780       // add a linear element
9781       nodes.resize( nbCornerNodes );
9782       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9783       ReplaceElemInGroups(elem, newElem, meshDS);
9784       if( theSm && newElem )
9785         theSm->AddElement( newElem );
9786     }
9787   }
9788   return nbElem;
9789 }
9790
9791 //=======================================================================
9792 //function : ConvertFromQuadratic
9793 //purpose  :
9794 //=======================================================================
9795
9796 bool SMESH_MeshEditor::ConvertFromQuadratic()
9797 {
9798   int nbCheckedElems = 0;
9799   if ( myMesh->HasShapeToMesh() )
9800   {
9801     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9802     {
9803       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9804       while ( smIt->more() ) {
9805         SMESH_subMesh* sm = smIt->next();
9806         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9807           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9808       }
9809     }
9810   }
9811
9812   int totalNbElems =
9813     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9814   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9815   {
9816     SMESHDS_SubMesh *aSM = 0;
9817     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9818   }
9819
9820   return true;
9821 }
9822
9823 namespace
9824 {
9825   //================================================================================
9826   /*!
9827    * \brief Return true if all medium nodes of the element are in the node set
9828    */
9829   //================================================================================
9830
9831   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9832   {
9833     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9834       if ( !nodeSet.count( elem->GetNode(i) ))
9835         return false;
9836     return true;
9837   }
9838 }
9839
9840 //================================================================================
9841 /*!
9842  * \brief Makes given elements linear
9843  */
9844 //================================================================================
9845
9846 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9847 {
9848   if ( theElements.empty() ) return;
9849
9850   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9851   set<int> mediumNodeIDs;
9852   TIDSortedElemSet::iterator eIt = theElements.begin();
9853   for ( ; eIt != theElements.end(); ++eIt )
9854   {
9855     const SMDS_MeshElement* e = *eIt;
9856     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9857       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9858   }
9859
9860   // replace given elements by linear ones
9861   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9862   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9863
9864   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9865   // except those elements sharing medium nodes of quadratic element whose medium nodes
9866   // are not all in mediumNodeIDs
9867
9868   // get remaining medium nodes
9869   TIDSortedNodeSet mediumNodes;
9870   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9871   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9872     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9873       mediumNodes.insert( mediumNodes.end(), n );
9874
9875   // find more quadratic elements to convert
9876   TIDSortedElemSet moreElemsToConvert;
9877   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9878   for ( ; nIt != mediumNodes.end(); ++nIt )
9879   {
9880     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9881     while ( invIt->more() )
9882     {
9883       const SMDS_MeshElement* e = invIt->next();
9884       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9885       {
9886         // find a more complex element including e and
9887         // whose medium nodes are not in mediumNodes
9888         bool complexFound = false;
9889         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9890         {
9891           SMDS_ElemIteratorPtr invIt2 =
9892             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9893           while ( invIt2->more() )
9894           {
9895             const SMDS_MeshElement* eComplex = invIt2->next();
9896             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9897             {
9898               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9899               if ( nbCommonNodes == e->NbNodes())
9900               {
9901                 complexFound = true;
9902                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9903                 break;
9904               }
9905             }
9906           }
9907         }
9908         if ( !complexFound )
9909           moreElemsToConvert.insert( e );
9910       }
9911     }
9912   }
9913   elemIt = elemSetIterator( moreElemsToConvert );
9914   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9915 }
9916
9917 //=======================================================================
9918 //function : SewSideElements
9919 //purpose  :
9920 //=======================================================================
9921
9922 SMESH_MeshEditor::Sew_Error
9923 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9924                                    TIDSortedElemSet&    theSide2,
9925                                    const SMDS_MeshNode* theFirstNode1,
9926                                    const SMDS_MeshNode* theFirstNode2,
9927                                    const SMDS_MeshNode* theSecondNode1,
9928                                    const SMDS_MeshNode* theSecondNode2)
9929 {
9930   myLastCreatedElems.Clear();
9931   myLastCreatedNodes.Clear();
9932
9933   if ( theSide1.size() != theSide2.size() )
9934     return SEW_DIFF_NB_OF_ELEMENTS;
9935
9936   Sew_Error aResult = SEW_OK;
9937   // Algo:
9938   // 1. Build set of faces representing each side
9939   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9940   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9941
9942   // =======================================================================
9943   // 1. Build set of faces representing each side:
9944   // =======================================================================
9945   // a. build set of nodes belonging to faces
9946   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9947   // c. create temporary faces representing side of volumes if correspondent
9948   //    face does not exist
9949
9950   SMESHDS_Mesh* aMesh = GetMeshDS();
9951   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9952   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9953   TIDSortedElemSet             faceSet1, faceSet2;
9954   set<const SMDS_MeshElement*> volSet1,  volSet2;
9955   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9956   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9957   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9958   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9959   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9960   int iSide, iFace, iNode;
9961
9962   list<const SMDS_MeshElement* > tempFaceList;
9963   for ( iSide = 0; iSide < 2; iSide++ ) {
9964     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9965     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9966     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9967     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9968     set<const SMDS_MeshElement*>::iterator vIt;
9969     TIDSortedElemSet::iterator eIt;
9970     set<const SMDS_MeshNode*>::iterator    nIt;
9971
9972     // check that given nodes belong to given elements
9973     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9974     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9975     int firstIndex = -1, secondIndex = -1;
9976     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9977       const SMDS_MeshElement* elem = *eIt;
9978       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9979       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9980       if ( firstIndex > -1 && secondIndex > -1 ) break;
9981     }
9982     if ( firstIndex < 0 || secondIndex < 0 ) {
9983       // we can simply return until temporary faces created
9984       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9985     }
9986
9987     // -----------------------------------------------------------
9988     // 1a. Collect nodes of existing faces
9989     //     and build set of face nodes in order to detect missing
9990     //     faces corresponding to sides of volumes
9991     // -----------------------------------------------------------
9992
9993     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9994
9995     // loop on the given element of a side
9996     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9997       //const SMDS_MeshElement* elem = *eIt;
9998       const SMDS_MeshElement* elem = *eIt;
9999       if ( elem->GetType() == SMDSAbs_Face ) {
10000         faceSet->insert( elem );
10001         set <const SMDS_MeshNode*> faceNodeSet;
10002         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10003         while ( nodeIt->more() ) {
10004           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10005           nodeSet->insert( n );
10006           faceNodeSet.insert( n );
10007         }
10008         setOfFaceNodeSet.insert( faceNodeSet );
10009       }
10010       else if ( elem->GetType() == SMDSAbs_Volume )
10011         volSet->insert( elem );
10012     }
10013     // ------------------------------------------------------------------------------
10014     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10015     // ------------------------------------------------------------------------------
10016
10017     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10018       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10019       while ( fIt->more() ) { // loop on faces sharing a node
10020         const SMDS_MeshElement* f = fIt->next();
10021         if ( faceSet->find( f ) == faceSet->end() ) {
10022           // check if all nodes are in nodeSet and
10023           // complete setOfFaceNodeSet if they are
10024           set <const SMDS_MeshNode*> faceNodeSet;
10025           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10026           bool allInSet = true;
10027           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10028             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10029             if ( nodeSet->find( n ) == nodeSet->end() )
10030               allInSet = false;
10031             else
10032               faceNodeSet.insert( n );
10033           }
10034           if ( allInSet ) {
10035             faceSet->insert( f );
10036             setOfFaceNodeSet.insert( faceNodeSet );
10037           }
10038         }
10039       }
10040     }
10041
10042     // -------------------------------------------------------------------------
10043     // 1c. Create temporary faces representing sides of volumes if correspondent
10044     //     face does not exist
10045     // -------------------------------------------------------------------------
10046
10047     if ( !volSet->empty() ) {
10048       //int nodeSetSize = nodeSet->size();
10049
10050       // loop on given volumes
10051       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10052         SMDS_VolumeTool vol (*vIt);
10053         // loop on volume faces: find free faces
10054         // --------------------------------------
10055         list<const SMDS_MeshElement* > freeFaceList;
10056         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10057           if ( !vol.IsFreeFace( iFace ))
10058             continue;
10059           // check if there is already a face with same nodes in a face set
10060           const SMDS_MeshElement* aFreeFace = 0;
10061           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10062           int nbNodes = vol.NbFaceNodes( iFace );
10063           set <const SMDS_MeshNode*> faceNodeSet;
10064           vol.GetFaceNodes( iFace, faceNodeSet );
10065           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10066           if ( isNewFace ) {
10067             // no such a face is given but it still can exist, check it
10068             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10069             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10070           }
10071           if ( !aFreeFace ) {
10072             // create a temporary face
10073             if ( nbNodes == 3 ) {
10074               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10075               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10076             }
10077             else if ( nbNodes == 4 ) {
10078               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10079               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10080             }
10081             else {
10082               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10083               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10084               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10085             }
10086             if ( aFreeFace )
10087               tempFaceList.push_back( aFreeFace );
10088           }
10089
10090           if ( aFreeFace )
10091             freeFaceList.push_back( aFreeFace );
10092
10093         } // loop on faces of a volume
10094
10095         // choose one of several free faces of a volume
10096         // --------------------------------------------
10097         if ( freeFaceList.size() > 1 ) {
10098           // choose a face having max nb of nodes shared by other elems of a side
10099           int maxNbNodes = -1;
10100           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10101           while ( fIt != freeFaceList.end() ) { // loop on free faces
10102             int nbSharedNodes = 0;
10103             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10104             while ( nodeIt->more() ) { // loop on free face nodes
10105               const SMDS_MeshNode* n =
10106                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10107               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10108               while ( invElemIt->more() ) {
10109                 const SMDS_MeshElement* e = invElemIt->next();
10110                 nbSharedNodes += faceSet->count( e );
10111                 nbSharedNodes += elemSet->count( e );
10112               }
10113             }
10114             if ( nbSharedNodes > maxNbNodes ) {
10115               maxNbNodes = nbSharedNodes;
10116               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10117             }
10118             else if ( nbSharedNodes == maxNbNodes ) {
10119               fIt++;
10120             }
10121             else {
10122               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10123             }
10124           }
10125           if ( freeFaceList.size() > 1 )
10126           {
10127             // could not choose one face, use another way
10128             // choose a face most close to the bary center of the opposite side
10129             gp_XYZ aBC( 0., 0., 0. );
10130             set <const SMDS_MeshNode*> addedNodes;
10131             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10132             eIt = elemSet2->begin();
10133             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10134               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10135               while ( nodeIt->more() ) { // loop on free face nodes
10136                 const SMDS_MeshNode* n =
10137                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10138                 if ( addedNodes.insert( n ).second )
10139                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10140               }
10141             }
10142             aBC /= addedNodes.size();
10143             double minDist = DBL_MAX;
10144             fIt = freeFaceList.begin();
10145             while ( fIt != freeFaceList.end() ) { // loop on free faces
10146               double dist = 0;
10147               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10148               while ( nodeIt->more() ) { // loop on free face nodes
10149                 const SMDS_MeshNode* n =
10150                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10151                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10152                 dist += ( aBC - p ).SquareModulus();
10153               }
10154               if ( dist < minDist ) {
10155                 minDist = dist;
10156                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10157               }
10158               else
10159                 fIt = freeFaceList.erase( fIt++ );
10160             }
10161           }
10162         } // choose one of several free faces of a volume
10163
10164         if ( freeFaceList.size() == 1 ) {
10165           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10166           faceSet->insert( aFreeFace );
10167           // complete a node set with nodes of a found free face
10168           //           for ( iNode = 0; iNode < ; iNode++ )
10169           //             nodeSet->insert( fNodes[ iNode ] );
10170         }
10171
10172       } // loop on volumes of a side
10173
10174       //       // complete a set of faces if new nodes in a nodeSet appeared
10175       //       // ----------------------------------------------------------
10176       //       if ( nodeSetSize != nodeSet->size() ) {
10177       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10178       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10179       //           while ( fIt->more() ) { // loop on faces sharing a node
10180       //             const SMDS_MeshElement* f = fIt->next();
10181       //             if ( faceSet->find( f ) == faceSet->end() ) {
10182       //               // check if all nodes are in nodeSet and
10183       //               // complete setOfFaceNodeSet if they are
10184       //               set <const SMDS_MeshNode*> faceNodeSet;
10185       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10186       //               bool allInSet = true;
10187       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10188       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10189       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10190       //                   allInSet = false;
10191       //                 else
10192       //                   faceNodeSet.insert( n );
10193       //               }
10194       //               if ( allInSet ) {
10195       //                 faceSet->insert( f );
10196       //                 setOfFaceNodeSet.insert( faceNodeSet );
10197       //               }
10198       //             }
10199       //           }
10200       //         }
10201       //       }
10202     } // Create temporary faces, if there are volumes given
10203   } // loop on sides
10204
10205   if ( faceSet1.size() != faceSet2.size() ) {
10206     // delete temporary faces: they are in reverseElements of actual nodes
10207 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10208 //    while ( tmpFaceIt->more() )
10209 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10210 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10211 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10212 //      aMesh->RemoveElement(*tmpFaceIt);
10213     MESSAGE("Diff nb of faces");
10214     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10215   }
10216
10217   // ============================================================
10218   // 2. Find nodes to merge:
10219   //              bind a node to remove to a node to put instead
10220   // ============================================================
10221
10222   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10223   if ( theFirstNode1 != theFirstNode2 )
10224     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10225   if ( theSecondNode1 != theSecondNode2 )
10226     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10227
10228   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10229   set< long > linkIdSet; // links to process
10230   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10231
10232   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10233   list< NLink > linkList[2];
10234   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10235   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10236   // loop on links in linkList; find faces by links and append links
10237   // of the found faces to linkList
10238   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10239   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10240   {
10241     NLink link[] = { *linkIt[0], *linkIt[1] };
10242     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10243     if ( !linkIdSet.count( linkID ) )
10244       continue;
10245
10246     // by links, find faces in the face sets,
10247     // and find indices of link nodes in the found faces;
10248     // in a face set, there is only one or no face sharing a link
10249     // ---------------------------------------------------------------
10250
10251     const SMDS_MeshElement* face[] = { 0, 0 };
10252     vector<const SMDS_MeshNode*> fnodes[2];
10253     int iLinkNode[2][2];
10254     TIDSortedElemSet avoidSet;
10255     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10256       const SMDS_MeshNode* n1 = link[iSide].first;
10257       const SMDS_MeshNode* n2 = link[iSide].second;
10258       //cout << "Side " << iSide << " ";
10259       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10260       // find a face by two link nodes
10261       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10262                                                       *faceSetPtr[ iSide ], avoidSet,
10263                                                       &iLinkNode[iSide][0],
10264                                                       &iLinkNode[iSide][1] );
10265       if ( face[ iSide ])
10266       {
10267         //cout << " F " << face[ iSide]->GetID() <<endl;
10268         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10269         // put face nodes to fnodes
10270         if ( face[ iSide ]->IsQuadratic() )
10271         {
10272           // use interlaced nodes iterator
10273           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10274           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10275           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10276           while ( nIter->more() )
10277             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10278         }
10279         else
10280         {
10281           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10282                                   face[ iSide ]->end_nodes() );
10283         }
10284         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10285       }
10286     }
10287
10288     // check similarity of elements of the sides
10289     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10290       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10291       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10292         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10293       }
10294       else {
10295         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10296       }
10297       break; // do not return because it's necessary to remove tmp faces
10298     }
10299
10300     // set nodes to merge
10301     // -------------------
10302
10303     if ( face[0] && face[1] )  {
10304       const int nbNodes = face[0]->NbNodes();
10305       if ( nbNodes != face[1]->NbNodes() ) {
10306         MESSAGE("Diff nb of face nodes");
10307         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10308         break; // do not return because it s necessary to remove tmp faces
10309       }
10310       bool reverse[] = { false, false }; // order of nodes in the link
10311       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10312         // analyse link orientation in faces
10313         int i1 = iLinkNode[ iSide ][ 0 ];
10314         int i2 = iLinkNode[ iSide ][ 1 ];
10315         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10316       }
10317       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10318       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10319       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10320       {
10321         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10322                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10323       }
10324
10325       // add other links of the faces to linkList
10326       // -----------------------------------------
10327
10328       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10329         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10330         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10331         if ( !iter_isnew.second ) { // already in a set: no need to process
10332           linkIdSet.erase( iter_isnew.first );
10333         }
10334         else // new in set == encountered for the first time: add
10335         {
10336           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10337           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10338           linkList[0].push_back ( NLink( n1, n2 ));
10339           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10340         }
10341       }
10342     } // 2 faces found
10343
10344     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10345       break;
10346
10347   } // loop on link lists
10348
10349   if ( aResult == SEW_OK &&
10350        ( //linkIt[0] != linkList[0].end() ||
10351          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10352     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10353              " " << (faceSetPtr[1]->empty()));
10354     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10355   }
10356
10357   // ====================================================================
10358   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10359   // ====================================================================
10360
10361   // delete temporary faces
10362 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10363 //  while ( tmpFaceIt->more() )
10364 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10365   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10366   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10367     aMesh->RemoveElement(*tmpFaceIt);
10368
10369   if ( aResult != SEW_OK)
10370     return aResult;
10371
10372   list< int > nodeIDsToRemove;
10373   vector< const SMDS_MeshNode*> nodes;
10374   ElemFeatures elemType;
10375
10376   // loop on nodes replacement map
10377   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10378   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10379     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10380     {
10381       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10382       nodeIDsToRemove.push_back( nToRemove->GetID() );
10383       // loop on elements sharing nToRemove
10384       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10385       while ( invElemIt->more() ) {
10386         const SMDS_MeshElement* e = invElemIt->next();
10387         // get a new suite of nodes: make replacement
10388         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10389         nodes.resize( nbNodes );
10390         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10391         while ( nIt->more() ) {
10392           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10393           nnIt = nReplaceMap.find( n );
10394           if ( nnIt != nReplaceMap.end() ) {
10395             nbReplaced++;
10396             n = (*nnIt).second;
10397           }
10398           nodes[ i++ ] = n;
10399         }
10400         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10401         //         elemIDsToRemove.push_back( e->GetID() );
10402         //       else
10403         if ( nbReplaced )
10404         {
10405           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10406           aMesh->RemoveElement( e );
10407
10408           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10409           {
10410             AddToSameGroups( newElem, e, aMesh );
10411             if ( int aShapeId = e->getshapeId() )
10412               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10413           }
10414         }
10415       }
10416     }
10417
10418   Remove( nodeIDsToRemove, true );
10419
10420   return aResult;
10421 }
10422
10423 //================================================================================
10424 /*!
10425  * \brief Find corresponding nodes in two sets of faces
10426  * \param theSide1 - first face set
10427  * \param theSide2 - second first face
10428  * \param theFirstNode1 - a boundary node of set 1
10429  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10430  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10431  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10432  * \param nReplaceMap - output map of corresponding nodes
10433  * \return bool  - is a success or not
10434  */
10435 //================================================================================
10436
10437 #ifdef _DEBUG_
10438 //#define DEBUG_MATCHING_NODES
10439 #endif
10440
10441 SMESH_MeshEditor::Sew_Error
10442 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10443                                     set<const SMDS_MeshElement*>& theSide2,
10444                                     const SMDS_MeshNode*          theFirstNode1,
10445                                     const SMDS_MeshNode*          theFirstNode2,
10446                                     const SMDS_MeshNode*          theSecondNode1,
10447                                     const SMDS_MeshNode*          theSecondNode2,
10448                                     TNodeNodeMap &                nReplaceMap)
10449 {
10450   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10451
10452   nReplaceMap.clear();
10453   if ( theFirstNode1 != theFirstNode2 )
10454     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10455   if ( theSecondNode1 != theSecondNode2 )
10456     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10457
10458   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10459   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10460
10461   list< NLink > linkList[2];
10462   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10463   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10464
10465   // loop on links in linkList; find faces by links and append links
10466   // of the found faces to linkList
10467   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10468   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10469     NLink link[] = { *linkIt[0], *linkIt[1] };
10470     if ( linkSet.find( link[0] ) == linkSet.end() )
10471       continue;
10472
10473     // by links, find faces in the face sets,
10474     // and find indices of link nodes in the found faces;
10475     // in a face set, there is only one or no face sharing a link
10476     // ---------------------------------------------------------------
10477
10478     const SMDS_MeshElement* face[] = { 0, 0 };
10479     list<const SMDS_MeshNode*> notLinkNodes[2];
10480     //bool reverse[] = { false, false }; // order of notLinkNodes
10481     int nbNodes[2];
10482     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10483     {
10484       const SMDS_MeshNode* n1 = link[iSide].first;
10485       const SMDS_MeshNode* n2 = link[iSide].second;
10486       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10487       set< const SMDS_MeshElement* > facesOfNode1;
10488       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10489       {
10490         // during a loop of the first node, we find all faces around n1,
10491         // during a loop of the second node, we find one face sharing both n1 and n2
10492         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10493         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10494         while ( fIt->more() ) { // loop on faces sharing a node
10495           const SMDS_MeshElement* f = fIt->next();
10496           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10497               ! facesOfNode1.insert( f ).second ) // f encounters twice
10498           {
10499             if ( face[ iSide ] ) {
10500               MESSAGE( "2 faces per link " );
10501               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10502             }
10503             face[ iSide ] = f;
10504             faceSet->erase( f );
10505
10506             // get not link nodes
10507             int nbN = f->NbNodes();
10508             if ( f->IsQuadratic() )
10509               nbN /= 2;
10510             nbNodes[ iSide ] = nbN;
10511             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10512             int i1 = f->GetNodeIndex( n1 );
10513             int i2 = f->GetNodeIndex( n2 );
10514             int iEnd = nbN, iBeg = -1, iDelta = 1;
10515             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10516             if ( reverse ) {
10517               std::swap( iEnd, iBeg ); iDelta = -1;
10518             }
10519             int i = i2;
10520             while ( true ) {
10521               i += iDelta;
10522               if ( i == iEnd ) i = iBeg + iDelta;
10523               if ( i == i1 ) break;
10524               nodes.push_back ( f->GetNode( i ) );
10525             }
10526           }
10527         }
10528       }
10529     }
10530     // check similarity of elements of the sides
10531     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10532       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10533       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10534         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10535       }
10536       else {
10537         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10538       }
10539     }
10540
10541     // set nodes to merge
10542     // -------------------
10543
10544     if ( face[0] && face[1] )  {
10545       if ( nbNodes[0] != nbNodes[1] ) {
10546         MESSAGE("Diff nb of face nodes");
10547         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10548       }
10549 #ifdef DEBUG_MATCHING_NODES
10550       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10551                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10552                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10553 #endif
10554       int nbN = nbNodes[0];
10555       {
10556         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10557         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10558         for ( int i = 0 ; i < nbN - 2; ++i ) {
10559 #ifdef DEBUG_MATCHING_NODES
10560           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10561 #endif
10562           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10563         }
10564       }
10565
10566       // add other links of the face 1 to linkList
10567       // -----------------------------------------
10568
10569       const SMDS_MeshElement* f0 = face[0];
10570       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10571       for ( int i = 0; i < nbN; i++ )
10572       {
10573         const SMDS_MeshNode* n2 = f0->GetNode( i );
10574         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10575           linkSet.insert( SMESH_TLink( n1, n2 ));
10576         if ( !iter_isnew.second ) { // already in a set: no need to process
10577           linkSet.erase( iter_isnew.first );
10578         }
10579         else // new in set == encountered for the first time: add
10580         {
10581 #ifdef DEBUG_MATCHING_NODES
10582           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10583                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10584 #endif
10585           linkList[0].push_back ( NLink( n1, n2 ));
10586           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10587         }
10588         n1 = n2;
10589       }
10590     } // 2 faces found
10591   } // loop on link lists
10592
10593   return SEW_OK;
10594 }
10595
10596 namespace // automatically find theAffectedElems for DoubleNodes()
10597 {
10598   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10599
10600   //--------------------------------------------------------------------------------
10601   // Nodes shared by adjacent FissureBorder's.
10602   // 1 node  if FissureBorder separates faces
10603   // 2 nodes if FissureBorder separates volumes
10604   struct SubBorder
10605   {
10606     const SMDS_MeshNode* _nodes[2];
10607     int                  _nbNodes;
10608
10609     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10610     {
10611       _nodes[0] = n1;
10612       _nodes[1] = n2;
10613       _nbNodes = bool( n1 ) + bool( n2 );
10614       if ( _nbNodes == 2 && n1 > n2 )
10615         std::swap( _nodes[0], _nodes[1] );
10616     }
10617     bool operator<( const SubBorder& other ) const
10618     {
10619       for ( int i = 0; i < _nbNodes; ++i )
10620       {
10621         if ( _nodes[i] < other._nodes[i] ) return true;
10622         if ( _nodes[i] > other._nodes[i] ) return false;
10623       }
10624       return false;
10625     }
10626   };
10627
10628   //--------------------------------------------------------------------------------
10629   // Map a SubBorder to all FissureBorder it bounds
10630   struct FissureBorder;
10631   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10632   typedef TBorderLinks::iterator                               TMappedSub;
10633
10634   //--------------------------------------------------------------------------------
10635   /*!
10636    * \brief Element border (volume facet or face edge) at a fissure
10637    */
10638   struct FissureBorder
10639   {
10640     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10641     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10642
10643     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10644     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10645
10646     FissureBorder( FissureBorder && from ) // move constructor
10647     {
10648       std::swap( _nodes,       from._nodes );
10649       std::swap( _sortedNodes, from._sortedNodes );
10650       _elems[0] = from._elems[0];
10651       _elems[1] = from._elems[1];
10652     }
10653
10654     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10655                    std::vector< const SMDS_MeshElement* > & adjElems)
10656       : _nodes( elemToDuplicate->NbCornerNodes() )
10657     {
10658       for ( size_t i = 0; i < _nodes.size(); ++i )
10659         _nodes[i] = elemToDuplicate->GetNode( i );
10660
10661       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10662       findAdjacent( type, adjElems );
10663     }
10664
10665     FissureBorder( const SMDS_MeshNode**                    nodes,
10666                    const size_t                             nbNodes,
10667                    const SMDSAbs_ElementType                adjElemsType,
10668                    std::vector< const SMDS_MeshElement* > & adjElems)
10669       : _nodes( nodes, nodes + nbNodes )
10670     {
10671       findAdjacent( adjElemsType, adjElems );
10672     }
10673
10674     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10675                        std::vector< const SMDS_MeshElement* > & adjElems)
10676     {
10677       _elems[0] = _elems[1] = 0;
10678       adjElems.clear();
10679       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10680         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10681           _elems[i] = adjElems[i];
10682     }
10683
10684     bool operator<( const FissureBorder& other ) const
10685     {
10686       return GetSortedNodes() < other.GetSortedNodes();
10687     }
10688
10689     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10690     {
10691       if ( _sortedNodes.empty() && !_nodes.empty() )
10692       {
10693         FissureBorder* me = const_cast<FissureBorder*>( this );
10694         me->_sortedNodes = me->_nodes;
10695         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10696       }
10697       return _sortedNodes;
10698     }
10699
10700     size_t NbSub() const
10701     {
10702       return _nodes.size();
10703     }
10704
10705     SubBorder Sub(size_t i) const
10706     {
10707       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10708     }
10709
10710     void AddSelfTo( TBorderLinks& borderLinks )
10711     {
10712       _mappedSubs.resize( NbSub() );
10713       for ( size_t i = 0; i < NbSub(); ++i )
10714       {
10715         TBorderLinks::iterator s2b =
10716           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10717         s2b->second.push_back( this );
10718         _mappedSubs[ i ] = s2b;
10719       }
10720     }
10721
10722     void Clear()
10723     {
10724       _nodes.clear();
10725     }
10726
10727     const SMDS_MeshElement* GetMarkedElem() const
10728     {
10729       if ( _nodes.empty() ) return 0; // cleared
10730       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10731       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10732       return 0;
10733     }
10734
10735     gp_XYZ GetNorm() const // normal to the border
10736     {
10737       gp_XYZ norm;
10738       if ( _nodes.size() == 2 )
10739       {
10740         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10741         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10742           avgNorm += norm;
10743         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10744           avgNorm += norm;
10745
10746         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10747         norm = bordDir ^ avgNorm;
10748       }
10749       else
10750       {
10751         SMESH_NodeXYZ p0( _nodes[0] );
10752         SMESH_NodeXYZ p1( _nodes[1] );
10753         SMESH_NodeXYZ p2( _nodes[2] );
10754         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10755       }
10756       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10757         norm.Reverse();
10758
10759       return norm;
10760     }
10761
10762     void ChooseSide() // mark an _elem located at positive side of fissure
10763     {
10764       _elems[0]->setIsMarked( true );
10765       gp_XYZ norm = GetNorm();
10766       double maxX = norm.Coord(1);
10767       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10768       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10769       if ( maxX < 0 )
10770       {
10771         _elems[0]->setIsMarked( false );
10772         _elems[1]->setIsMarked( true );
10773       }
10774     }
10775
10776   }; // struct FissureBorder
10777
10778   //--------------------------------------------------------------------------------
10779   /*!
10780    * \brief Classifier of elements at fissure edge
10781    */
10782   class FissureNormal
10783   {
10784     std::vector< gp_XYZ > _normals;
10785     bool                  _bothIn;
10786
10787   public:
10788     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10789     {
10790       _bothIn = false;
10791       _normals.reserve(2);
10792       _normals.push_back( bord.GetNorm() );
10793       if ( _normals.size() == 2 )
10794         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10795     }
10796
10797     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10798     {
10799       bool isIn = false;
10800       switch ( _normals.size() ) {
10801       case 1:
10802       {
10803         isIn = !isOut( n, _normals[0], elem );
10804         break;
10805       }
10806       case 2:
10807       {
10808         bool in1 = !isOut( n, _normals[0], elem );
10809         bool in2 = !isOut( n, _normals[1], elem );
10810         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10811       }
10812       }
10813       return isIn;
10814     }
10815   };
10816
10817   //================================================================================
10818   /*!
10819    * \brief Classify an element by a plane passing through a node
10820    */
10821   //================================================================================
10822
10823   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10824   {
10825     SMESH_NodeXYZ p = n;
10826     double sumDot = 0;
10827     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10828     {
10829       SMESH_NodeXYZ pi = elem->GetNode( i );
10830       sumDot += norm * ( pi - p );
10831     }
10832     return sumDot < -1e-100;
10833   }
10834
10835   //================================================================================
10836   /*!
10837    * \brief Find FissureBorder's by nodes to duplicate
10838    */
10839   //================================================================================
10840
10841   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10842                            std::vector< FissureBorder > & theFissureBorders )
10843   {
10844     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10845     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10846     if ( !n ) return;
10847     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10848     if ( n->NbInverseElements( elemType ) == 0 )
10849     {
10850       elemType = SMDSAbs_Face;
10851       if ( n->NbInverseElements( elemType ) == 0 )
10852         return;
10853     }
10854     // unmark elements touching the fissure
10855     for ( ; nIt != theNodes.end(); ++nIt )
10856       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10857
10858     // loop on elements touching the fissure to get their borders belonging to the fissure
10859     std::set< FissureBorder >              fissureBorders;
10860     std::vector< const SMDS_MeshElement* > adjElems;
10861     std::vector< const SMDS_MeshNode* >    nodes;
10862     SMDS_VolumeTool volTool;
10863     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10864     {
10865       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10866       while ( invIt->more() )
10867       {
10868         const SMDS_MeshElement* eInv = invIt->next();
10869         if ( eInv->isMarked() ) continue;
10870         eInv->setIsMarked( true );
10871
10872         if ( elemType == SMDSAbs_Volume )
10873         {
10874           volTool.Set( eInv );
10875           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10876           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10877           {
10878             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10879             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10880             nodes.clear();
10881             bool allOnFissure = true;
10882             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10883               if (( allOnFissure = theNodes.count( nn[ iN ])))
10884                 nodes.push_back( nn[ iN ]);
10885             if ( allOnFissure )
10886               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10887                                                                elemType, adjElems )));
10888           }
10889         }
10890         else // elemType == SMDSAbs_Face
10891         {
10892           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10893           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10894           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10895           {
10896             nn[1]      = eInv->GetNode( iN );
10897             onFissure1 = theNodes.count( nn[1] );
10898             if ( onFissure0 && onFissure1 )
10899               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10900             nn[0]      = nn[1];
10901             onFissure0 = onFissure1;
10902           }
10903         }
10904       }
10905     }
10906
10907     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10908     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10909     for ( ; bord != fissureBorders.end(); ++bord )
10910     {
10911       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10912     }
10913     return;
10914   } // findFissureBorders()
10915
10916   //================================================================================
10917   /*!
10918    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10919    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10920    *  \param [in] theNodesNot - nodes not to duplicate
10921    *  \param [out] theAffectedElems - the found elements
10922    */
10923   //================================================================================
10924
10925   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10926                           TIDSortedElemSet&       theAffectedElems)
10927   {
10928     if ( theElemsOrNodes.empty() ) return;
10929
10930     // find FissureBorder's
10931
10932     std::vector< FissureBorder >           fissure;
10933     std::vector< const SMDS_MeshElement* > elemsByFacet;
10934
10935     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10936     if ( (*elIt)->GetType() == SMDSAbs_Node )
10937     {
10938       findFissureBorders( theElemsOrNodes, fissure );
10939     }
10940     else
10941     {
10942       fissure.reserve( theElemsOrNodes.size() );
10943       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10944         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10945     }
10946     if ( fissure.empty() )
10947       return;
10948
10949     // fill borderLinks
10950
10951     TBorderLinks borderLinks;
10952
10953     for ( size_t i = 0; i < fissure.size(); ++i )
10954     {
10955       fissure[i].AddSelfTo( borderLinks );
10956     }
10957
10958     // get theAffectedElems
10959
10960     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10961     for ( size_t i = 0; i < fissure.size(); ++i )
10962       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10963       {
10964         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10965                                         false, /*markElem=*/true );
10966       }
10967
10968     std::vector<const SMDS_MeshNode *>                 facetNodes;
10969     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10970     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10971
10972     // choose a side of fissure
10973     fissure[0].ChooseSide();
10974     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10975
10976     size_t nbCheckedBorders = 0;
10977     while ( nbCheckedBorders < fissure.size() )
10978     {
10979       // find a FissureBorder to treat
10980       FissureBorder* bord = 0;
10981       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10982         if ( fissure[i].GetMarkedElem() )
10983           bord = & fissure[i];
10984       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10985         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10986         {
10987           bord = & fissure[i];
10988           bord->ChooseSide();
10989           theAffectedElems.insert( bord->GetMarkedElem() );
10990         }
10991       if ( !bord ) return;
10992       ++nbCheckedBorders;
10993
10994       // treat FissureBorder's linked to bord
10995       fissureNodes.clear();
10996       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10997       for ( size_t i = 0; i < bord->NbSub(); ++i )
10998       {
10999         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
11000         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
11001         std::vector< FissureBorder* >& linkedBorders = l2b->second;
11002         const SubBorder&                          sb = l2b->first;
11003         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
11004
11005         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
11006         {
11007           for ( int j = 0; j < sb._nbNodes; ++j )
11008             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
11009           continue;
11010         }
11011
11012         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
11013         // until an elem adjacent to a neighbour FissureBorder is found
11014         facetNodes.clear();
11015         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
11016         facetNodes.resize( sb._nbNodes + 1 );
11017
11018         while ( bordElem )
11019         {
11020           // check if bordElem is adjacent to a neighbour FissureBorder
11021           for ( size_t j = 0; j < linkedBorders.size(); ++j )
11022           {
11023             FissureBorder* bord2 = linkedBorders[j];
11024             if ( bord2 == bord ) continue;
11025             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
11026               bordElem = 0;
11027             else
11028               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
11029           }
11030           if ( !bordElem )
11031             break;
11032
11033           // find the next bordElem
11034           const SMDS_MeshElement* nextBordElem = 0;
11035           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
11036           {
11037             const SMDS_MeshNode* n = bordElem->GetNode( iN );
11038             if ( fissureNodes.count( n )) continue;
11039
11040             facetNodes[ sb._nbNodes ] = n;
11041             elemsByFacet.clear();
11042             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
11043             {
11044               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
11045                 if ( elemsByFacet[ iE ] != bordElem &&
11046                      !elemsByFacet[ iE ]->isMarked() )
11047                 {
11048                   theAffectedElems.insert( elemsByFacet[ iE ]);
11049                   elemsByFacet[ iE ]->setIsMarked( true );
11050                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
11051                     nextBordElem = elemsByFacet[ iE ];
11052                 }
11053             }
11054           }
11055           bordElem = nextBordElem;
11056
11057         } // while ( bordElem )
11058
11059         linkedBorders.clear(); // not to treat this link any more
11060
11061       } // loop on SubBorder's of a FissureBorder
11062
11063       bord->Clear();
11064
11065     } // loop on FissureBorder's
11066
11067
11068     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
11069
11070     // mark nodes of theAffectedElems
11071     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
11072
11073     // unmark nodes of the fissure
11074     elIt = theElemsOrNodes.begin();
11075     if ( (*elIt)->GetType() == SMDSAbs_Node )
11076       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
11077     else
11078       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
11079
11080     std::vector< gp_XYZ > normVec;
11081
11082     // loop on nodes of the fissure, add elements having marked nodes
11083     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
11084     {
11085       const SMDS_MeshElement* e = (*elIt);
11086       if ( e->GetType() != SMDSAbs_Node )
11087         e->setIsMarked( true ); // avoid adding a fissure element
11088
11089       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
11090       {
11091         const SMDS_MeshNode* n = e->GetNode( iN );
11092         if ( fissEdgeNodes2Norm.count( n ))
11093           continue;
11094
11095         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
11096         while ( invIt->more() )
11097         {
11098           const SMDS_MeshElement* eInv = invIt->next();
11099           if ( eInv->isMarked() ) continue;
11100           eInv->setIsMarked( true );
11101
11102           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
11103           while( nIt->more() )
11104             if ( nIt->next()->isMarked())
11105             {
11106               theAffectedElems.insert( eInv );
11107               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
11108               n->setIsMarked( false );
11109               break;
11110             }
11111         }
11112       }
11113     }
11114
11115     // add elements on the fissure edge
11116     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
11117     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
11118     {
11119       const SMDS_MeshNode* edgeNode = n2N->first;
11120       const FissureNormal & normals = n2N->second;
11121
11122       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
11123       while ( invIt->more() )
11124       {
11125         const SMDS_MeshElement* eInv = invIt->next();
11126         if ( eInv->isMarked() ) continue;
11127         eInv->setIsMarked( true );
11128
11129         // classify eInv using normals
11130         bool toAdd = normals.IsIn( edgeNode, eInv );
11131         if ( toAdd ) // check if all nodes lie on the fissure edge
11132         {
11133           bool notOnEdge = false;
11134           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
11135             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
11136           toAdd = notOnEdge;
11137         }
11138         if ( toAdd )
11139         {
11140           theAffectedElems.insert( eInv );
11141         }
11142       }
11143     }
11144
11145     return;
11146   } // findAffectedElems()
11147 } // namespace
11148
11149 //================================================================================
11150 /*!
11151  * \brief Create elements equal (on same nodes) to given ones
11152  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
11153  *              elements of the uppest dimension are duplicated.
11154  */
11155 //================================================================================
11156
11157 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
11158 {
11159   ClearLastCreated();
11160   SMESHDS_Mesh* mesh = GetMeshDS();
11161
11162   // get an element type and an iterator over elements
11163
11164   SMDSAbs_ElementType type = SMDSAbs_All;
11165   SMDS_ElemIteratorPtr elemIt;
11166   if ( theElements.empty() )
11167   {
11168     if ( mesh->NbNodes() == 0 )
11169       return;
11170     // get most complex type
11171     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
11172       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
11173       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
11174     };
11175     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
11176       if ( mesh->GetMeshInfo().NbElements( types[i] ))
11177       {
11178         type = types[i];
11179         break;
11180       }
11181     elemIt = mesh->elementsIterator( type );
11182   }
11183   else
11184   {
11185     type = (*theElements.begin())->GetType();
11186     elemIt = elemSetIterator( theElements );
11187   }
11188
11189   // duplicate elements
11190
11191   ElemFeatures elemType;
11192
11193   vector< const SMDS_MeshNode* > nodes;
11194   while ( elemIt->more() )
11195   {
11196     const SMDS_MeshElement* elem = elemIt->next();
11197     if ( elem->GetType() != type )
11198       continue;
11199
11200     elemType.Init( elem, /*basicOnly=*/false );
11201     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11202
11203     AddElement( nodes, elemType );
11204   }
11205 }
11206
11207 //================================================================================
11208 /*!
11209   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11210   \param theElems - the list of elements (edges or faces) to be replicated
11211   The nodes for duplication could be found from these elements
11212   \param theNodesNot - list of nodes to NOT replicate
11213   \param theAffectedElems - the list of elements (cells and edges) to which the
11214   replicated nodes should be associated to.
11215   \return TRUE if operation has been completed successfully, FALSE otherwise
11216 */
11217 //================================================================================
11218
11219 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11220                                     const TIDSortedElemSet& theNodesNot,
11221                                     const TIDSortedElemSet& theAffectedElems )
11222 {
11223   ClearLastCreated();
11224
11225   if ( theElems.size() == 0 )
11226     return false;
11227
11228   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11229   if ( !aMeshDS )
11230     return false;
11231
11232   bool res = false;
11233   TNodeNodeMap anOldNodeToNewNode;
11234   // duplicate elements and nodes
11235   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11236   // replce nodes by duplications
11237   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11238   return res;
11239 }
11240
11241 //================================================================================
11242 /*!
11243   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11244   \param theMeshDS - mesh instance
11245   \param theElems - the elements replicated or modified (nodes should be changed)
11246   \param theNodesNot - nodes to NOT replicate
11247   \param theNodeNodeMap - relation of old node to new created node
11248   \param theIsDoubleElem - flag os to replicate element or modify
11249   \return TRUE if operation has been completed successfully, FALSE otherwise
11250 */
11251 //================================================================================
11252
11253 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11254                                    const TIDSortedElemSet& theElems,
11255                                    const TIDSortedElemSet& theNodesNot,
11256                                    TNodeNodeMap&           theNodeNodeMap,
11257                                    const bool              theIsDoubleElem )
11258 {
11259   // iterate through element and duplicate them (by nodes duplication)
11260   bool res = false;
11261   std::vector<const SMDS_MeshNode*> newNodes;
11262   ElemFeatures elemType;
11263
11264   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11265   for ( ;  elemItr != theElems.end(); ++elemItr )
11266   {
11267     const SMDS_MeshElement* anElem = *elemItr;
11268     // if (!anElem)
11269     //   continue;
11270
11271     // duplicate nodes to duplicate element
11272     bool isDuplicate = false;
11273     newNodes.resize( anElem->NbNodes() );
11274     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11275     int ind = 0;
11276     while ( anIter->more() )
11277     {
11278       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11279       const SMDS_MeshNode*  aNewNode = aCurrNode;
11280       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11281       if ( n2n != theNodeNodeMap.end() )
11282       {
11283         aNewNode = n2n->second;
11284       }
11285       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11286       {
11287         // duplicate node
11288         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11289         copyPosition( aCurrNode, aNewNode );
11290         theNodeNodeMap[ aCurrNode ] = aNewNode;
11291         myLastCreatedNodes.Append( aNewNode );
11292       }
11293       isDuplicate |= (aCurrNode != aNewNode);
11294       newNodes[ ind++ ] = aNewNode;
11295     }
11296     if ( !isDuplicate )
11297       continue;
11298
11299     if ( theIsDoubleElem )
11300       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11301     else
11302       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11303
11304     res = true;
11305   }
11306   return res;
11307 }
11308
11309 //================================================================================
11310 /*!
11311   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11312   \param theNodes - identifiers of nodes to be doubled
11313   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11314   nodes. If list of element identifiers is empty then nodes are doubled but
11315   they not assigned to elements
11316   \return TRUE if operation has been completed successfully, FALSE otherwise
11317 */
11318 //================================================================================
11319
11320 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11321                                     const std::list< int >& theListOfModifiedElems )
11322 {
11323   ClearLastCreated();
11324
11325   if ( theListOfNodes.size() == 0 )
11326     return false;
11327
11328   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11329   if ( !aMeshDS )
11330     return false;
11331
11332   // iterate through nodes and duplicate them
11333
11334   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11335
11336   std::list< int >::const_iterator aNodeIter;
11337   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11338   {
11339     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11340     if ( !aNode )
11341       continue;
11342
11343     // duplicate node
11344
11345     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11346     if ( aNewNode )
11347     {
11348       copyPosition( aNode, aNewNode );
11349       anOldNodeToNewNode[ aNode ] = aNewNode;
11350       myLastCreatedNodes.Append( aNewNode );
11351     }
11352   }
11353
11354   // Change nodes of elements
11355
11356   std::vector<const SMDS_MeshNode*> aNodeArr;
11357
11358   std::list< int >::const_iterator anElemIter;
11359   for ( anElemIter =  theListOfModifiedElems.begin();
11360         anElemIter != theListOfModifiedElems.end();
11361         anElemIter++ )
11362   {
11363     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11364     if ( !anElem )
11365       continue;
11366
11367     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11368     for( size_t i = 0; i < aNodeArr.size(); ++i )
11369     {
11370       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11371         anOldNodeToNewNode.find( aNodeArr[ i ]);
11372       if ( n2n != anOldNodeToNewNode.end() )
11373         aNodeArr[ i ] = n2n->second;
11374     }
11375     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11376   }
11377
11378   return true;
11379 }
11380
11381 namespace {
11382
11383   //================================================================================
11384   /*!
11385   \brief Check if element located inside shape
11386   \return TRUE if IN or ON shape, FALSE otherwise
11387   */
11388   //================================================================================
11389
11390   template<class Classifier>
11391   bool isInside(const SMDS_MeshElement* theElem,
11392                 Classifier&             theClassifier,
11393                 const double            theTol)
11394   {
11395     gp_XYZ centerXYZ (0, 0, 0);
11396     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11397     while ( aNodeItr->more() )
11398       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11399
11400     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11401     theClassifier.Perform(aPnt, theTol);
11402     TopAbs_State aState = theClassifier.State();
11403     return (aState == TopAbs_IN || aState == TopAbs_ON );
11404   }
11405
11406   //================================================================================
11407   /*!
11408    * \brief Classifier of the 3D point on the TopoDS_Face
11409    *        with interaface suitable for isInside()
11410    */
11411   //================================================================================
11412
11413   struct _FaceClassifier
11414   {
11415     Extrema_ExtPS       _extremum;
11416     BRepAdaptor_Surface _surface;
11417     TopAbs_State        _state;
11418
11419     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11420     {
11421       _extremum.Initialize( _surface,
11422                             _surface.FirstUParameter(), _surface.LastUParameter(),
11423                             _surface.FirstVParameter(), _surface.LastVParameter(),
11424                             _surface.Tolerance(), _surface.Tolerance() );
11425     }
11426     void Perform(const gp_Pnt& aPnt, double theTol)
11427     {
11428       theTol *= theTol;
11429       _state = TopAbs_OUT;
11430       _extremum.Perform(aPnt);
11431       if ( _extremum.IsDone() )
11432         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11433           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11434     }
11435     TopAbs_State State() const
11436     {
11437       return _state;
11438     }
11439   };
11440 }
11441
11442 //================================================================================
11443 /*!
11444   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11445   This method is the first step of DoubleNodeElemGroupsInRegion.
11446   \param theElems - list of groups of elements (edges or faces) to be replicated
11447   \param theNodesNot - list of groups of nodes not to replicated
11448   \param theShape - shape to detect affected elements (element which geometric center
11449          located on or inside shape). If the shape is null, detection is done on faces orientations
11450          (select elements with a gravity center on the side given by faces normals).
11451          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11452          The replicated nodes should be associated to affected elements.
11453   \return true
11454   \sa DoubleNodeElemGroupsInRegion()
11455 */
11456 //================================================================================
11457
11458 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11459                                                    const TIDSortedElemSet& theNodesNot,
11460                                                    const TopoDS_Shape&     theShape,
11461                                                    TIDSortedElemSet&       theAffectedElems)
11462 {
11463   if ( theShape.IsNull() )
11464   {
11465     findAffectedElems( theElems, theAffectedElems );
11466   }
11467   else
11468   {
11469     const double aTol = Precision::Confusion();
11470     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11471     auto_ptr<_FaceClassifier>              aFaceClassifier;
11472     if ( theShape.ShapeType() == TopAbs_SOLID )
11473     {
11474       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11475       bsc3d->PerformInfinitePoint(aTol);
11476     }
11477     else if (theShape.ShapeType() == TopAbs_FACE )
11478     {
11479       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11480     }
11481
11482     // iterates on indicated elements and get elements by back references from their nodes
11483     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11484     for ( ;  elemItr != theElems.end(); ++elemItr )
11485     {
11486       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11487       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11488       while ( nodeItr->more() )
11489       {
11490         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11491         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11492           continue;
11493         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11494         while ( backElemItr->more() )
11495         {
11496           const SMDS_MeshElement* curElem = backElemItr->next();
11497           if ( curElem && theElems.find(curElem) == theElems.end() &&
11498                ( bsc3d.get() ?
11499                  isInside( curElem, *bsc3d, aTol ) :
11500                  isInside( curElem, *aFaceClassifier, aTol )))
11501             theAffectedElems.insert( curElem );
11502         }
11503       }
11504     }
11505   }
11506   return true;
11507 }
11508
11509 //================================================================================
11510 /*!
11511   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11512   \param theElems - group of of elements (edges or faces) to be replicated
11513   \param theNodesNot - group of nodes not to replicate
11514   \param theShape - shape to detect affected elements (element which geometric center
11515   located on or inside shape).
11516   The replicated nodes should be associated to affected elements.
11517   \return TRUE if operation has been completed successfully, FALSE otherwise
11518 */
11519 //================================================================================
11520
11521 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11522                                             const TIDSortedElemSet& theNodesNot,
11523                                             const TopoDS_Shape&     theShape )
11524 {
11525   if ( theShape.IsNull() )
11526     return false;
11527
11528   const double aTol = Precision::Confusion();
11529   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11530   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11531   if ( theShape.ShapeType() == TopAbs_SOLID )
11532   {
11533     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11534     bsc3d->PerformInfinitePoint(aTol);
11535   }
11536   else if (theShape.ShapeType() == TopAbs_FACE )
11537   {
11538     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11539   }
11540
11541   // iterates on indicated elements and get elements by back references from their nodes
11542   TIDSortedElemSet anAffected;
11543   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11544   for ( ;  elemItr != theElems.end(); ++elemItr )
11545   {
11546     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11547     if (!anElem)
11548       continue;
11549
11550     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11551     while ( nodeItr->more() )
11552     {
11553       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11554       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11555         continue;
11556       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11557       while ( backElemItr->more() )
11558       {
11559         const SMDS_MeshElement* curElem = backElemItr->next();
11560         if ( curElem && theElems.find(curElem) == theElems.end() &&
11561              ( bsc3d ?
11562                isInside( curElem, *bsc3d, aTol ) :
11563                isInside( curElem, *aFaceClassifier, aTol )))
11564           anAffected.insert( curElem );
11565       }
11566     }
11567   }
11568   return DoubleNodes( theElems, theNodesNot, anAffected );
11569 }
11570
11571 /*!
11572  *  \brief compute an oriented angle between two planes defined by four points.
11573  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11574  *  @param p0 base of the rotation axe
11575  *  @param p1 extremity of the rotation axe
11576  *  @param g1 belongs to the first plane
11577  *  @param g2 belongs to the second plane
11578  */
11579 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11580 {
11581   gp_Vec vref(p0, p1);
11582   gp_Vec v1(p0, g1);
11583   gp_Vec v2(p0, g2);
11584   gp_Vec n1 = vref.Crossed(v1);
11585   gp_Vec n2 = vref.Crossed(v2);
11586   try {
11587     return n2.AngleWithRef(n1, vref);
11588   }
11589   catch ( Standard_Failure ) {
11590   }
11591   return Max( v1.Magnitude(), v2.Magnitude() );
11592 }
11593
11594 /*!
11595  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11596  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11597  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11598  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11599  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11600  * 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.
11601  * 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.
11602  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11603  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11604  * \param theElems - list of groups of volumes, where a group of volume is a set of
11605  *        SMDS_MeshElements sorted by Id.
11606  * \param createJointElems - if TRUE, create the elements
11607  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11608  *        the boundary between \a theDomains and the rest mesh
11609  * \return TRUE if operation has been completed successfully, FALSE otherwise
11610  */
11611 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11612                                                      bool                                 createJointElems,
11613                                                      bool                                 onAllBoundaries)
11614 {
11615   // MESSAGE("----------------------------------------------");
11616   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11617   // MESSAGE("----------------------------------------------");
11618
11619   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11620   meshDS->BuildDownWardConnectivity(true);
11621   CHRONO(50);
11622   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11623
11624   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11625   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11626   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11627
11628   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11629   std::map<int,int>celldom; // cell vtkId --> domain
11630   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11631   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11632   faceDomains.clear();
11633   celldom.clear();
11634   cellDomains.clear();
11635   nodeDomains.clear();
11636   std::map<int,int> emptyMap;
11637   std::set<int> emptySet;
11638   emptyMap.clear();
11639
11640   //MESSAGE(".. Number of domains :"<<theElems.size());
11641
11642   TIDSortedElemSet theRestDomElems;
11643   const int iRestDom  = -1;
11644   const int idom0     = onAllBoundaries ? iRestDom : 0;
11645   const int nbDomains = theElems.size();
11646
11647   // Check if the domains do not share an element
11648   for (int idom = 0; idom < nbDomains-1; idom++)
11649   {
11650     //       MESSAGE("... Check of domain #" << idom);
11651     const TIDSortedElemSet& domain = theElems[idom];
11652     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11653     for (; elemItr != domain.end(); ++elemItr)
11654     {
11655       const SMDS_MeshElement* anElem = *elemItr;
11656       int idombisdeb = idom + 1 ;
11657       // check if the element belongs to a domain further in the list
11658       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11659       {
11660         const TIDSortedElemSet& domainbis = theElems[idombis];
11661         if ( domainbis.count( anElem ))
11662         {
11663           MESSAGE(".... Domain #" << idom);
11664           MESSAGE(".... Domain #" << idombis);
11665           throw SALOME_Exception("The domains are not disjoint.");
11666           return false ;
11667         }
11668       }
11669     }
11670   }
11671
11672   for (int idom = 0; idom < nbDomains; idom++)
11673   {
11674
11675     // --- build a map (face to duplicate --> volume to modify)
11676     //     with all the faces shared by 2 domains (group of elements)
11677     //     and corresponding volume of this domain, for each shared face.
11678     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11679
11680     //MESSAGE("... Neighbors of domain #" << idom);
11681     const TIDSortedElemSet& domain = theElems[idom];
11682     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11683     for (; elemItr != domain.end(); ++elemItr)
11684     {
11685       const SMDS_MeshElement* anElem = *elemItr;
11686       if (!anElem)
11687         continue;
11688       int vtkId = anElem->getVtkId();
11689       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11690       int neighborsVtkIds[NBMAXNEIGHBORS];
11691       int downIds[NBMAXNEIGHBORS];
11692       unsigned char downTypes[NBMAXNEIGHBORS];
11693       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11694       for (int n = 0; n < nbNeighbors; n++)
11695       {
11696         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11697         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11698         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11699         {
11700           bool ok = false;
11701           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11702           {
11703             // MESSAGE("Domain " << idombis);
11704             const TIDSortedElemSet& domainbis = theElems[idombis];
11705             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11706           }
11707           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11708           {
11709             DownIdType face(downIds[n], downTypes[n]);
11710             if (!faceDomains[face].count(idom))
11711             {
11712               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11713               celldom[vtkId] = idom;
11714               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11715             }
11716             if ( !ok )
11717             {
11718               theRestDomElems.insert( elem );
11719               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11720               celldom[neighborsVtkIds[n]] = iRestDom;
11721             }
11722           }
11723         }
11724       }
11725     }
11726   }
11727
11728   //MESSAGE("Number of shared faces " << faceDomains.size());
11729   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11730
11731   // --- explore the shared faces domain by domain,
11732   //     explore the nodes of the face and see if they belong to a cell in the domain,
11733   //     which has only a node or an edge on the border (not a shared face)
11734
11735   for (int idomain = idom0; idomain < nbDomains; idomain++)
11736   {
11737     //MESSAGE("Domain " << idomain);
11738     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11739     itface = faceDomains.begin();
11740     for (; itface != faceDomains.end(); ++itface)
11741     {
11742       const std::map<int, int>& domvol = itface->second;
11743       if (!domvol.count(idomain))
11744         continue;
11745       DownIdType face = itface->first;
11746       //MESSAGE(" --- face " << face.cellId);
11747       std::set<int> oldNodes;
11748       oldNodes.clear();
11749       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11750       std::set<int>::iterator itn = oldNodes.begin();
11751       for (; itn != oldNodes.end(); ++itn)
11752       {
11753         int oldId = *itn;
11754         //MESSAGE("     node " << oldId);
11755         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11756         for (int i=0; i<l.ncells; i++)
11757         {
11758           int vtkId = l.cells[i];
11759           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11760           if (!domain.count(anElem))
11761             continue;
11762           int vtkType = grid->GetCellType(vtkId);
11763           int downId = grid->CellIdToDownId(vtkId);
11764           if (downId < 0)
11765           {
11766             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11767             continue; // not OK at this stage of the algorithm:
11768             //no cells created after BuildDownWardConnectivity
11769           }
11770           DownIdType aCell(downId, vtkType);
11771           cellDomains[aCell][idomain] = vtkId;
11772           celldom[vtkId] = idomain;
11773           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11774         }
11775       }
11776     }
11777   }
11778
11779   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11780   //     for each shared face, get the nodes
11781   //     for each node, for each domain of the face, create a clone of the node
11782
11783   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11784   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11785   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11786
11787   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11788   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11789   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11790
11791   //MESSAGE(".. Duplication of the nodes");
11792   for (int idomain = idom0; idomain < nbDomains; idomain++)
11793   {
11794     itface = faceDomains.begin();
11795     for (; itface != faceDomains.end(); ++itface)
11796     {
11797       const std::map<int, int>& domvol = itface->second;
11798       if (!domvol.count(idomain))
11799         continue;
11800       DownIdType face = itface->first;
11801       //MESSAGE(" --- face " << face.cellId);
11802       std::set<int> oldNodes;
11803       oldNodes.clear();
11804       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11805       std::set<int>::iterator itn = oldNodes.begin();
11806       for (; itn != oldNodes.end(); ++itn)
11807       {
11808         int oldId = *itn;
11809         if (nodeDomains[oldId].empty())
11810         {
11811           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11812           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11813         }
11814         std::map<int, int>::const_iterator itdom = domvol.begin();
11815         for (; itdom != domvol.end(); ++itdom)
11816         {
11817           int idom = itdom->first;
11818           //MESSAGE("         domain " << idom);
11819           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11820           {
11821             if (nodeDomains[oldId].size() >= 2) // a multiple node
11822             {
11823               vector<int> orderedDoms;
11824               //MESSAGE("multiple node " << oldId);
11825               if (mutipleNodes.count(oldId))
11826                 orderedDoms = mutipleNodes[oldId];
11827               else
11828               {
11829                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11830                 for (; it != nodeDomains[oldId].end(); ++it)
11831                   orderedDoms.push_back(it->first);
11832               }
11833               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11834               //stringstream txt;
11835               //for (int i=0; i<orderedDoms.size(); i++)
11836               //  txt << orderedDoms[i] << " ";
11837               //MESSAGE("orderedDoms " << txt.str());
11838               mutipleNodes[oldId] = orderedDoms;
11839             }
11840             double *coords = grid->GetPoint(oldId);
11841             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11842             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11843             int newId = newNode->getVtkId();
11844             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11845             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11846           }
11847         }
11848       }
11849     }
11850   }
11851
11852   //MESSAGE(".. Creation of elements");
11853   for (int idomain = idom0; idomain < nbDomains; idomain++)
11854   {
11855     itface = faceDomains.begin();
11856     for (; itface != faceDomains.end(); ++itface)
11857     {
11858       std::map<int, int> domvol = itface->second;
11859       if (!domvol.count(idomain))
11860         continue;
11861       DownIdType face = itface->first;
11862       //MESSAGE(" --- face " << face.cellId);
11863       std::set<int> oldNodes;
11864       oldNodes.clear();
11865       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11866       int nbMultipleNodes = 0;
11867       std::set<int>::iterator itn = oldNodes.begin();
11868       for (; itn != oldNodes.end(); ++itn)
11869       {
11870         int oldId = *itn;
11871         if (mutipleNodes.count(oldId))
11872           nbMultipleNodes++;
11873       }
11874       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11875       {
11876         //MESSAGE("multiple Nodes detected on a shared face");
11877         int downId = itface->first.cellId;
11878         unsigned char cellType = itface->first.cellType;
11879         // --- shared edge or shared face ?
11880         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11881         {
11882           int nodes[3];
11883           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11884           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11885             if (mutipleNodes.count(nodes[i]))
11886               if (!mutipleNodesToFace.count(nodes[i]))
11887                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11888         }
11889         else // shared face (between two volumes)
11890         {
11891           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11892           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11893           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11894           for (int ie =0; ie < nbEdges; ie++)
11895           {
11896             int nodes[3];
11897             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11898             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11899             {
11900               vector<int> vn0 = mutipleNodes[nodes[0]];
11901               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11902               vector<int> doms;
11903               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11904                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11905                   if ( vn0[i0] == vn1[i1] )
11906                     doms.push_back( vn0[ i0 ]);
11907               if ( doms.size() > 2 )
11908               {
11909                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11910                 double *coords = grid->GetPoint(nodes[0]);
11911                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11912                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11913                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11914                 gp_Pnt gref;
11915                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11916                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11917                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11918                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11919                 for ( size_t id = 0; id < doms.size(); id++ )
11920                 {
11921                   int idom = doms[id];
11922                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11923                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11924                   {
11925                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11926                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11927                     if (domain.count(elem))
11928                     {
11929                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11930                       domvol[idom] = svol;
11931                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11932                       double values[3];
11933                       vtkIdType npts = 0;
11934                       vtkIdType* pts = 0;
11935                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11936                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11937                       if (id ==0)
11938                       {
11939                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11940                         angleDom[idom] = 0;
11941                       }
11942                       else
11943                       {
11944                         gp_Pnt g(values[0], values[1], values[2]);
11945                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11946                         //MESSAGE("  angle=" << angleDom[idom]);
11947                       }
11948                       break;
11949                     }
11950                   }
11951                 }
11952                 map<double, int> sortedDom; // sort domains by angle
11953                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11954                   sortedDom[ia->second] = ia->first;
11955                 vector<int> vnodes;
11956                 vector<int> vdom;
11957                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11958                 {
11959                   vdom.push_back(ib->second);
11960                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11961                 }
11962                 for (int ino = 0; ino < nbNodes; ino++)
11963                   vnodes.push_back(nodes[ino]);
11964                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11965               }
11966             }
11967           }
11968         }
11969       }
11970     }
11971   }
11972
11973   // --- iterate on shared faces (volumes to modify, face to extrude)
11974   //     get node id's of the face (id SMDS = id VTK)
11975   //     create flat element with old and new nodes if requested
11976
11977   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11978   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11979
11980   std::map<int, std::map<long,int> > nodeQuadDomains;
11981   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11982
11983   //MESSAGE(".. Creation of elements: simple junction");
11984   if (createJointElems)
11985   {
11986     int idg;
11987     string joints2DName = "joints2D";
11988     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11989     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11990     string joints3DName = "joints3D";
11991     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11992     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11993
11994     itface = faceDomains.begin();
11995     for (; itface != faceDomains.end(); ++itface)
11996     {
11997       DownIdType face = itface->first;
11998       std::set<int> oldNodes;
11999       std::set<int>::iterator itn;
12000       oldNodes.clear();
12001       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12002
12003       std::map<int, int> domvol = itface->second;
12004       std::map<int, int>::iterator itdom = domvol.begin();
12005       int dom1 = itdom->first;
12006       int vtkVolId = itdom->second;
12007       itdom++;
12008       int dom2 = itdom->first;
12009       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
12010                                                        nodeQuadDomains);
12011       stringstream grpname;
12012       grpname << "j_";
12013       if (dom1 < dom2)
12014         grpname << dom1 << "_" << dom2;
12015       else
12016         grpname << dom2 << "_" << dom1;
12017       string namegrp = grpname.str();
12018       if (!mapOfJunctionGroups.count(namegrp))
12019         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
12020       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12021       if (sgrp)
12022         sgrp->Add(vol->GetID());
12023       if (vol->GetType() == SMDSAbs_Volume)
12024         joints3DGrp->Add(vol->GetID());
12025       else if (vol->GetType() == SMDSAbs_Face)
12026         joints2DGrp->Add(vol->GetID());
12027     }
12028   }
12029
12030   // --- create volumes on multiple domain intersection if requested
12031   //     iterate on mutipleNodesToFace
12032   //     iterate on edgesMultiDomains
12033
12034   //MESSAGE(".. Creation of elements: multiple junction");
12035   if (createJointElems)
12036   {
12037     // --- iterate on mutipleNodesToFace
12038
12039     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
12040     for (; itn != mutipleNodesToFace.end(); ++itn)
12041     {
12042       int node = itn->first;
12043       vector<int> orderDom = itn->second;
12044       vector<vtkIdType> orderedNodes;
12045       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12046         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
12047       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
12048
12049       stringstream grpname;
12050       grpname << "m2j_";
12051       grpname << 0 << "_" << 0;
12052       int idg;
12053       string namegrp = grpname.str();
12054       if (!mapOfJunctionGroups.count(namegrp))
12055         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
12056       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12057       if (sgrp)
12058         sgrp->Add(face->GetID());
12059     }
12060
12061     // --- iterate on edgesMultiDomains
12062
12063     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
12064     for (; ite != edgesMultiDomains.end(); ++ite)
12065     {
12066       vector<int> nodes = ite->first;
12067       vector<int> orderDom = ite->second;
12068       vector<vtkIdType> orderedNodes;
12069       if (nodes.size() == 2)
12070       {
12071         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
12072         for ( size_t ino = 0; ino < nodes.size(); ino++ )
12073           if ( orderDom.size() == 3 )
12074             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12075               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12076           else
12077             for (int idom = orderDom.size()-1; idom >=0; idom--)
12078               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12079         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
12080
12081         int idg;
12082         string namegrp = "jointsMultiples";
12083         if (!mapOfJunctionGroups.count(namegrp))
12084           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12085         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12086         if (sgrp)
12087           sgrp->Add(vol->GetID());
12088       }
12089       else
12090       {
12091         //INFOS("Quadratic multiple joints not implemented");
12092         // TODO quadratic nodes
12093       }
12094     }
12095   }
12096
12097   // --- list the explicit faces and edges of the mesh that need to be modified,
12098   //     i.e. faces and edges built with one or more duplicated nodes.
12099   //     associate these faces or edges to their corresponding domain.
12100   //     only the first domain found is kept when a face or edge is shared
12101
12102   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
12103   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
12104   faceOrEdgeDom.clear();
12105   feDom.clear();
12106
12107   //MESSAGE(".. Modification of elements");
12108   for (int idomain = idom0; idomain < nbDomains; idomain++)
12109   {
12110     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
12111     for (; itnod != nodeDomains.end(); ++itnod)
12112     {
12113       int oldId = itnod->first;
12114       //MESSAGE("     node " << oldId);
12115       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
12116       for (int i = 0; i < l.ncells; i++)
12117       {
12118         int vtkId = l.cells[i];
12119         int vtkType = grid->GetCellType(vtkId);
12120         int downId = grid->CellIdToDownId(vtkId);
12121         if (downId < 0)
12122           continue; // new cells: not to be modified
12123         DownIdType aCell(downId, vtkType);
12124         int volParents[1000];
12125         int nbvol = grid->GetParentVolumes(volParents, vtkId);
12126         for (int j = 0; j < nbvol; j++)
12127           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
12128             if (!feDom.count(vtkId))
12129             {
12130               feDom[vtkId] = idomain;
12131               faceOrEdgeDom[aCell] = emptyMap;
12132               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
12133               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
12134               //        << " type " << vtkType << " downId " << downId);
12135             }
12136       }
12137     }
12138   }
12139
12140   // --- iterate on shared faces (volumes to modify, face to extrude)
12141   //     get node id's of the face
12142   //     replace old nodes by new nodes in volumes, and update inverse connectivity
12143
12144   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
12145   for (int m=0; m<3; m++)
12146   {
12147     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
12148     itface = (*amap).begin();
12149     for (; itface != (*amap).end(); ++itface)
12150     {
12151       DownIdType face = itface->first;
12152       std::set<int> oldNodes;
12153       std::set<int>::iterator itn;
12154       oldNodes.clear();
12155       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12156       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12157       std::map<int, int> localClonedNodeIds;
12158
12159       std::map<int, int> domvol = itface->second;
12160       std::map<int, int>::iterator itdom = domvol.begin();
12161       for (; itdom != domvol.end(); ++itdom)
12162       {
12163         int idom = itdom->first;
12164         int vtkVolId = itdom->second;
12165         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
12166         localClonedNodeIds.clear();
12167         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12168         {
12169           int oldId = *itn;
12170           if (nodeDomains[oldId].count(idom))
12171           {
12172             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12173             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
12174           }
12175         }
12176         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12177       }
12178     }
12179   }
12180
12181   // Remove empty groups (issue 0022812)
12182   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12183   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12184   {
12185     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12186       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12187   }
12188
12189   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12190   grid->DeleteLinks();
12191
12192   CHRONOSTOP(50);
12193   counters::stats();
12194   return true;
12195 }
12196
12197 /*!
12198  * \brief Double nodes on some external faces and create flat elements.
12199  * Flat elements are mainly used by some types of mechanic calculations.
12200  *
12201  * Each group of the list must be constituted of faces.
12202  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12203  * @param theElems - list of groups of faces, where a group of faces is a set of
12204  * SMDS_MeshElements sorted by Id.
12205  * @return TRUE if operation has been completed successfully, FALSE otherwise
12206  */
12207 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12208 {
12209   // MESSAGE("-------------------------------------------------");
12210   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12211   // MESSAGE("-------------------------------------------------");
12212
12213   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12214
12215   // --- For each group of faces
12216   //     duplicate the nodes, create a flat element based on the face
12217   //     replace the nodes of the faces by their clones
12218
12219   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12220   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12221   clonedNodes.clear();
12222   intermediateNodes.clear();
12223   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12224   mapOfJunctionGroups.clear();
12225
12226   for ( size_t idom = 0; idom < theElems.size(); idom++ )
12227   {
12228     const TIDSortedElemSet&           domain = theElems[idom];
12229     TIDSortedElemSet::const_iterator elemItr = domain.begin();
12230     for ( ; elemItr != domain.end(); ++elemItr )
12231     {
12232       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
12233       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
12234       if (!aFace)
12235         continue;
12236       // MESSAGE("aFace=" << aFace->GetID());
12237       bool isQuad = aFace->IsQuadratic();
12238       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12239
12240       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12241
12242       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
12243       while (nodeIt->more())
12244       {
12245         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
12246         bool isMedium = isQuad && (aFace->IsMediumNode(node));
12247         if (isMedium)
12248           ln2.push_back(node);
12249         else
12250           ln0.push_back(node);
12251
12252         const SMDS_MeshNode* clone = 0;
12253         if (!clonedNodes.count(node))
12254         {
12255           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12256           copyPosition( node, clone );
12257           clonedNodes[node] = clone;
12258         }
12259         else
12260           clone = clonedNodes[node];
12261
12262         if (isMedium)
12263           ln3.push_back(clone);
12264         else
12265           ln1.push_back(clone);
12266
12267         const SMDS_MeshNode* inter = 0;
12268         if (isQuad && (!isMedium))
12269         {
12270           if (!intermediateNodes.count(node))
12271           {
12272             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12273             copyPosition( node, inter );
12274             intermediateNodes[node] = inter;
12275           }
12276           else
12277             inter = intermediateNodes[node];
12278           ln4.push_back(inter);
12279         }
12280       }
12281
12282       // --- extrude the face
12283
12284       vector<const SMDS_MeshNode*> ln;
12285       SMDS_MeshVolume* vol = 0;
12286       vtkIdType aType = aFace->GetVtkType();
12287       switch (aType)
12288       {
12289       case VTK_TRIANGLE:
12290         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12291         // MESSAGE("vol prism " << vol->GetID());
12292         ln.push_back(ln1[0]);
12293         ln.push_back(ln1[1]);
12294         ln.push_back(ln1[2]);
12295         break;
12296       case VTK_QUAD:
12297         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12298         // MESSAGE("vol hexa " << vol->GetID());
12299         ln.push_back(ln1[0]);
12300         ln.push_back(ln1[1]);
12301         ln.push_back(ln1[2]);
12302         ln.push_back(ln1[3]);
12303         break;
12304       case VTK_QUADRATIC_TRIANGLE:
12305         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12306                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12307         // MESSAGE("vol quad prism " << vol->GetID());
12308         ln.push_back(ln1[0]);
12309         ln.push_back(ln1[1]);
12310         ln.push_back(ln1[2]);
12311         ln.push_back(ln3[0]);
12312         ln.push_back(ln3[1]);
12313         ln.push_back(ln3[2]);
12314         break;
12315       case VTK_QUADRATIC_QUAD:
12316         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12317         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12318         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12319         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12320                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12321                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12322         // MESSAGE("vol quad hexa " << vol->GetID());
12323         ln.push_back(ln1[0]);
12324         ln.push_back(ln1[1]);
12325         ln.push_back(ln1[2]);
12326         ln.push_back(ln1[3]);
12327         ln.push_back(ln3[0]);
12328         ln.push_back(ln3[1]);
12329         ln.push_back(ln3[2]);
12330         ln.push_back(ln3[3]);
12331         break;
12332       case VTK_POLYGON:
12333         break;
12334       default:
12335         break;
12336       }
12337
12338       if (vol)
12339       {
12340         stringstream grpname;
12341         grpname << "jf_";
12342         grpname << idom;
12343         int idg;
12344         string namegrp = grpname.str();
12345         if (!mapOfJunctionGroups.count(namegrp))
12346           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12347         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12348         if (sgrp)
12349           sgrp->Add(vol->GetID());
12350       }
12351
12352       // --- modify the face
12353
12354       aFace->ChangeNodes(&ln[0], ln.size());
12355     }
12356   }
12357   return true;
12358 }
12359
12360 /*!
12361  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12362  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12363  *  groups of faces to remove inside the object, (idem edges).
12364  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12365  */
12366 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12367                                       const TopoDS_Shape&             theShape,
12368                                       SMESH_NodeSearcher*             theNodeSearcher,
12369                                       const char*                     groupName,
12370                                       std::vector<double>&            nodesCoords,
12371                                       std::vector<std::vector<int> >& listOfListOfNodes)
12372 {
12373   // MESSAGE("--------------------------------");
12374   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12375   // MESSAGE("--------------------------------");
12376
12377   // --- zone of volumes to remove is given :
12378   //     1 either by a geom shape (one or more vertices) and a radius,
12379   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12380   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12381   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12382   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12383   //     defined by it's name.
12384
12385   SMESHDS_GroupBase* groupDS = 0;
12386   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12387   while ( groupIt->more() )
12388   {
12389     groupDS = 0;
12390     SMESH_Group * group = groupIt->next();
12391     if ( !group ) continue;
12392     groupDS = group->GetGroupDS();
12393     if ( !groupDS || groupDS->IsEmpty() ) continue;
12394     std::string grpName = group->GetName();
12395     //MESSAGE("grpName=" << grpName);
12396     if (grpName == groupName)
12397       break;
12398     else
12399       groupDS = 0;
12400   }
12401
12402   bool isNodeGroup = false;
12403   bool isNodeCoords = false;
12404   if (groupDS)
12405   {
12406     if (groupDS->GetType() != SMDSAbs_Node)
12407       return;
12408     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12409   }
12410
12411   if (nodesCoords.size() > 0)
12412     isNodeCoords = true; // a list o nodes given by their coordinates
12413   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12414
12415   // --- define groups to build
12416
12417   int idg; // --- group of SMDS volumes
12418   string grpvName = groupName;
12419   grpvName += "_vol";
12420   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12421   if (!grp)
12422   {
12423     MESSAGE("group not created " << grpvName);
12424     return;
12425   }
12426   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12427
12428   int idgs; // --- group of SMDS faces on the skin
12429   string grpsName = groupName;
12430   grpsName += "_skin";
12431   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12432   if (!grps)
12433   {
12434     MESSAGE("group not created " << grpsName);
12435     return;
12436   }
12437   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12438
12439   int idgi; // --- group of SMDS faces internal (several shapes)
12440   string grpiName = groupName;
12441   grpiName += "_internalFaces";
12442   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12443   if (!grpi)
12444   {
12445     MESSAGE("group not created " << grpiName);
12446     return;
12447   }
12448   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12449
12450   int idgei; // --- group of SMDS faces internal (several shapes)
12451   string grpeiName = groupName;
12452   grpeiName += "_internalEdges";
12453   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12454   if (!grpei)
12455   {
12456     MESSAGE("group not created " << grpeiName);
12457     return;
12458   }
12459   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12460
12461   // --- build downward connectivity
12462
12463   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12464   meshDS->BuildDownWardConnectivity(true);
12465   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12466
12467   // --- set of volumes detected inside
12468
12469   std::set<int> setOfInsideVol;
12470   std::set<int> setOfVolToCheck;
12471
12472   std::vector<gp_Pnt> gpnts;
12473   gpnts.clear();
12474
12475   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12476   {
12477     //MESSAGE("group of nodes provided");
12478     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12479     while ( elemIt->more() )
12480     {
12481       const SMDS_MeshElement* elem = elemIt->next();
12482       if (!elem)
12483         continue;
12484       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12485       if (!node)
12486         continue;
12487       SMDS_MeshElement* vol = 0;
12488       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12489       while (volItr->more())
12490       {
12491         vol = (SMDS_MeshElement*)volItr->next();
12492         setOfInsideVol.insert(vol->getVtkId());
12493         sgrp->Add(vol->GetID());
12494       }
12495     }
12496   }
12497   else if (isNodeCoords)
12498   {
12499     //MESSAGE("list of nodes coordinates provided");
12500     size_t i = 0;
12501     int k = 0;
12502     while ( i < nodesCoords.size()-2 )
12503     {
12504       double x = nodesCoords[i++];
12505       double y = nodesCoords[i++];
12506       double z = nodesCoords[i++];
12507       gp_Pnt p = gp_Pnt(x, y ,z);
12508       gpnts.push_back(p);
12509       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12510       k++;
12511     }
12512   }
12513   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12514   {
12515     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12516     TopTools_IndexedMapOfShape vertexMap;
12517     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12518     gp_Pnt p = gp_Pnt(0,0,0);
12519     if (vertexMap.Extent() < 1)
12520       return;
12521
12522     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12523     {
12524       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12525       p = BRep_Tool::Pnt(vertex);
12526       gpnts.push_back(p);
12527       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12528     }
12529   }
12530
12531   if (gpnts.size() > 0)
12532   {
12533     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12534     //MESSAGE("startNode->nodeId " << nodeId);
12535
12536     double radius2 = radius*radius;
12537     //MESSAGE("radius2 " << radius2);
12538
12539     // --- volumes on start node
12540
12541     setOfVolToCheck.clear();
12542     SMDS_MeshElement* startVol = 0;
12543     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12544     while (volItr->more())
12545     {
12546       startVol = (SMDS_MeshElement*)volItr->next();
12547       setOfVolToCheck.insert(startVol->getVtkId());
12548     }
12549     if (setOfVolToCheck.empty())
12550     {
12551       MESSAGE("No volumes found");
12552       return;
12553     }
12554
12555     // --- starting with central volumes then their neighbors, check if they are inside
12556     //     or outside the domain, until no more new neighbor volume is inside.
12557     //     Fill the group of inside volumes
12558
12559     std::map<int, double> mapOfNodeDistance2;
12560     mapOfNodeDistance2.clear();
12561     std::set<int> setOfOutsideVol;
12562     while (!setOfVolToCheck.empty())
12563     {
12564       std::set<int>::iterator it = setOfVolToCheck.begin();
12565       int vtkId = *it;
12566       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12567       bool volInside = false;
12568       vtkIdType npts = 0;
12569       vtkIdType* pts = 0;
12570       grid->GetCellPoints(vtkId, npts, pts);
12571       for (int i=0; i<npts; i++)
12572       {
12573         double distance2 = 0;
12574         if (mapOfNodeDistance2.count(pts[i]))
12575         {
12576           distance2 = mapOfNodeDistance2[pts[i]];
12577           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12578         }
12579         else
12580         {
12581           double *coords = grid->GetPoint(pts[i]);
12582           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12583           distance2 = 1.E40;
12584           for ( size_t j = 0; j < gpnts.size(); j++ )
12585           {
12586             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12587             if (d2 < distance2)
12588             {
12589               distance2 = d2;
12590               if (distance2 < radius2)
12591                 break;
12592             }
12593           }
12594           mapOfNodeDistance2[pts[i]] = distance2;
12595           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12596         }
12597         if (distance2 < radius2)
12598         {
12599           volInside = true; // one or more nodes inside the domain
12600           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12601           break;
12602         }
12603       }
12604       if (volInside)
12605       {
12606         setOfInsideVol.insert(vtkId);
12607         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12608         int neighborsVtkIds[NBMAXNEIGHBORS];
12609         int downIds[NBMAXNEIGHBORS];
12610         unsigned char downTypes[NBMAXNEIGHBORS];
12611         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12612         for (int n = 0; n < nbNeighbors; n++)
12613           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12614             setOfVolToCheck.insert(neighborsVtkIds[n]);
12615       }
12616       else
12617       {
12618         setOfOutsideVol.insert(vtkId);
12619         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12620       }
12621       setOfVolToCheck.erase(vtkId);
12622     }
12623   }
12624
12625   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12626   //     If yes, add the volume to the inside set
12627
12628   bool addedInside = true;
12629   std::set<int> setOfVolToReCheck;
12630   while (addedInside)
12631   {
12632     //MESSAGE(" --------------------------- re check");
12633     addedInside = false;
12634     std::set<int>::iterator itv = setOfInsideVol.begin();
12635     for (; itv != setOfInsideVol.end(); ++itv)
12636     {
12637       int vtkId = *itv;
12638       int neighborsVtkIds[NBMAXNEIGHBORS];
12639       int downIds[NBMAXNEIGHBORS];
12640       unsigned char downTypes[NBMAXNEIGHBORS];
12641       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12642       for (int n = 0; n < nbNeighbors; n++)
12643         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12644           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12645     }
12646     setOfVolToCheck = setOfVolToReCheck;
12647     setOfVolToReCheck.clear();
12648     while  (!setOfVolToCheck.empty())
12649     {
12650       std::set<int>::iterator it = setOfVolToCheck.begin();
12651       int vtkId = *it;
12652       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12653       {
12654         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12655         int countInside = 0;
12656         int neighborsVtkIds[NBMAXNEIGHBORS];
12657         int downIds[NBMAXNEIGHBORS];
12658         unsigned char downTypes[NBMAXNEIGHBORS];
12659         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12660         for (int n = 0; n < nbNeighbors; n++)
12661           if (setOfInsideVol.count(neighborsVtkIds[n]))
12662             countInside++;
12663         //MESSAGE("countInside " << countInside);
12664         if (countInside > 1)
12665         {
12666           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12667           setOfInsideVol.insert(vtkId);
12668           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12669           addedInside = true;
12670         }
12671         else
12672           setOfVolToReCheck.insert(vtkId);
12673       }
12674       setOfVolToCheck.erase(vtkId);
12675     }
12676   }
12677
12678   // --- map of Downward faces at the boundary, inside the global volume
12679   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12680   //     fill group of SMDS faces inside the volume (when several volume shapes)
12681   //     fill group of SMDS faces on the skin of the global volume (if skin)
12682
12683   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12684   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12685   std::set<int>::iterator it = setOfInsideVol.begin();
12686   for (; it != setOfInsideVol.end(); ++it)
12687   {
12688     int vtkId = *it;
12689     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12690     int neighborsVtkIds[NBMAXNEIGHBORS];
12691     int downIds[NBMAXNEIGHBORS];
12692     unsigned char downTypes[NBMAXNEIGHBORS];
12693     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12694     for (int n = 0; n < nbNeighbors; n++)
12695     {
12696       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12697       if (neighborDim == 3)
12698       {
12699         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12700         {
12701           DownIdType face(downIds[n], downTypes[n]);
12702           boundaryFaces[face] = vtkId;
12703         }
12704         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12705         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12706         if (vtkFaceId >= 0)
12707         {
12708           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12709           // find also the smds edges on this face
12710           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12711           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12712           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12713           for (int i = 0; i < nbEdges; i++)
12714           {
12715             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12716             if (vtkEdgeId >= 0)
12717               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12718           }
12719         }
12720       }
12721       else if (neighborDim == 2) // skin of the volume
12722       {
12723         DownIdType face(downIds[n], downTypes[n]);
12724         skinFaces[face] = vtkId;
12725         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12726         if (vtkFaceId >= 0)
12727           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12728       }
12729     }
12730   }
12731
12732   // --- identify the edges constituting the wire of each subshape on the skin
12733   //     define polylines with the nodes of edges, equivalent to wires
12734   //     project polylines on subshapes, and partition, to get geom faces
12735
12736   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12737   std::set<int> emptySet;
12738   emptySet.clear();
12739   std::set<int> shapeIds;
12740
12741   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12742   while (itelem->more())
12743   {
12744     const SMDS_MeshElement *elem = itelem->next();
12745     int shapeId = elem->getshapeId();
12746     int vtkId = elem->getVtkId();
12747     if (!shapeIdToVtkIdSet.count(shapeId))
12748     {
12749       shapeIdToVtkIdSet[shapeId] = emptySet;
12750       shapeIds.insert(shapeId);
12751     }
12752     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12753   }
12754
12755   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12756   std::set<DownIdType, DownIdCompare> emptyEdges;
12757   emptyEdges.clear();
12758
12759   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12760   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12761   {
12762     int shapeId = itShape->first;
12763     //MESSAGE(" --- Shape ID --- "<< shapeId);
12764     shapeIdToEdges[shapeId] = emptyEdges;
12765
12766     std::vector<int> nodesEdges;
12767
12768     std::set<int>::iterator its = itShape->second.begin();
12769     for (; its != itShape->second.end(); ++its)
12770     {
12771       int vtkId = *its;
12772       //MESSAGE("     " << vtkId);
12773       int neighborsVtkIds[NBMAXNEIGHBORS];
12774       int downIds[NBMAXNEIGHBORS];
12775       unsigned char downTypes[NBMAXNEIGHBORS];
12776       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12777       for (int n = 0; n < nbNeighbors; n++)
12778       {
12779         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12780           continue;
12781         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12782         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12783         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12784         {
12785           DownIdType edge(downIds[n], downTypes[n]);
12786           if (!shapeIdToEdges[shapeId].count(edge))
12787           {
12788             shapeIdToEdges[shapeId].insert(edge);
12789             int vtkNodeId[3];
12790             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12791             nodesEdges.push_back(vtkNodeId[0]);
12792             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12793             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12794           }
12795         }
12796       }
12797     }
12798
12799     std::list<int> order;
12800     order.clear();
12801     if (nodesEdges.size() > 0)
12802     {
12803       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12804       nodesEdges[0] = -1;
12805       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12806       nodesEdges[1] = -1; // do not reuse this edge
12807       bool found = true;
12808       while (found)
12809       {
12810         int nodeTofind = order.back(); // try first to push back
12811         int i = 0;
12812         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12813           if (nodesEdges[i] == nodeTofind)
12814             break;
12815         if ( i == (int) nodesEdges.size() )
12816           found = false; // no follower found on back
12817         else
12818         {
12819           if (i%2) // odd ==> use the previous one
12820             if (nodesEdges[i-1] < 0)
12821               found = false;
12822             else
12823             {
12824               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12825               nodesEdges[i-1] = -1;
12826             }
12827           else // even ==> use the next one
12828             if (nodesEdges[i+1] < 0)
12829               found = false;
12830             else
12831             {
12832               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12833               nodesEdges[i+1] = -1;
12834             }
12835         }
12836         if (found)
12837           continue;
12838         // try to push front
12839         found = true;
12840         nodeTofind = order.front(); // try to push front
12841         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12842           if ( nodesEdges[i] == nodeTofind )
12843             break;
12844         if ( i == (int)nodesEdges.size() )
12845         {
12846           found = false; // no predecessor found on front
12847           continue;
12848         }
12849         if (i%2) // odd ==> use the previous one
12850           if (nodesEdges[i-1] < 0)
12851             found = false;
12852           else
12853           {
12854             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12855             nodesEdges[i-1] = -1;
12856           }
12857         else // even ==> use the next one
12858           if (nodesEdges[i+1] < 0)
12859             found = false;
12860           else
12861           {
12862             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12863             nodesEdges[i+1] = -1;
12864           }
12865       }
12866     }
12867
12868
12869     std::vector<int> nodes;
12870     nodes.push_back(shapeId);
12871     std::list<int>::iterator itl = order.begin();
12872     for (; itl != order.end(); itl++)
12873     {
12874       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12875       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12876     }
12877     listOfListOfNodes.push_back(nodes);
12878   }
12879
12880   //     partition geom faces with blocFissure
12881   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12882   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12883
12884   return;
12885 }
12886
12887
12888 //================================================================================
12889 /*!
12890  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12891  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12892  * \return TRUE if operation has been completed successfully, FALSE otherwise
12893  */
12894 //================================================================================
12895
12896 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12897 {
12898   // iterates on volume elements and detect all free faces on them
12899   SMESHDS_Mesh* aMesh = GetMeshDS();
12900   if (!aMesh)
12901     return false;
12902
12903   ElemFeatures faceType( SMDSAbs_Face );
12904   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12905   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12906   while(vIt->more())
12907   {
12908     const SMDS_MeshVolume* volume = vIt->next();
12909     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12910     vTool.SetExternalNormal();
12911     const int iQuad = volume->IsQuadratic();
12912     faceType.SetQuad( iQuad );
12913     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12914     {
12915       if (!vTool.IsFreeFace(iface))
12916         continue;
12917       nbFree++;
12918       vector<const SMDS_MeshNode *> nodes;
12919       int nbFaceNodes = vTool.NbFaceNodes(iface);
12920       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12921       int inode = 0;
12922       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12923         nodes.push_back(faceNodes[inode]);
12924
12925       if (iQuad) // add medium nodes
12926       {
12927         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12928           nodes.push_back(faceNodes[inode]);
12929         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12930           nodes.push_back(faceNodes[8]);
12931       }
12932       // add new face based on volume nodes
12933       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12934       {
12935         nbExisted++; // face already exsist
12936       }
12937       else
12938       {
12939         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12940         nbCreated++;
12941       }
12942     }
12943   }
12944   return ( nbFree == ( nbExisted + nbCreated ));
12945 }
12946
12947 namespace
12948 {
12949   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12950   {
12951     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12952       return n;
12953     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12954   }
12955 }
12956 //================================================================================
12957 /*!
12958  * \brief Creates missing boundary elements
12959  *  \param elements - elements whose boundary is to be checked
12960  *  \param dimension - defines type of boundary elements to create
12961  *  \param group - a group to store created boundary elements in
12962  *  \param targetMesh - a mesh to store created boundary elements in
12963  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12964  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12965  *                                boundary elements will be copied into the targetMesh
12966  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12967  *                                boundary elements will be added into the new group
12968  *  \param aroundElements - if true, elements will be created on boundary of given
12969  *                          elements else, on boundary of the whole mesh.
12970  * \return nb of added boundary elements
12971  */
12972 //================================================================================
12973
12974 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12975                                        Bnd_Dimension           dimension,
12976                                        SMESH_Group*            group/*=0*/,
12977                                        SMESH_Mesh*             targetMesh/*=0*/,
12978                                        bool                    toCopyElements/*=false*/,
12979                                        bool                    toCopyExistingBoundary/*=false*/,
12980                                        bool                    toAddExistingBondary/*= false*/,
12981                                        bool                    aroundElements/*= false*/)
12982 {
12983   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12984   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12985   // hope that all elements are of the same type, do not check them all
12986   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12987     throw SALOME_Exception(LOCALIZED("wrong element type"));
12988
12989   if ( !targetMesh )
12990     toCopyElements = toCopyExistingBoundary = false;
12991
12992   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12993   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12994   int nbAddedBnd = 0;
12995
12996   // editor adding present bnd elements and optionally holding elements to add to the group
12997   SMESH_MeshEditor* presentEditor;
12998   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12999   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
13000
13001   SMESH_MesherHelper helper( *myMesh );
13002   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
13003   SMDS_VolumeTool vTool;
13004   TIDSortedElemSet avoidSet;
13005   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
13006   size_t inode;
13007
13008   typedef vector<const SMDS_MeshNode*> TConnectivity;
13009   TConnectivity tgtNodes;
13010   ElemFeatures elemKind( missType ), elemToCopy;
13011
13012   vector<const SMDS_MeshElement*> presentBndElems;
13013   vector<TConnectivity>           missingBndElems;
13014   vector<int>                     freeFacets;
13015   TConnectivity nodes, elemNodes;
13016
13017   SMDS_ElemIteratorPtr eIt;
13018   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13019   else                  eIt = elemSetIterator( elements );
13020
13021   while (eIt->more())
13022   {
13023     const SMDS_MeshElement* elem = eIt->next();
13024     const int              iQuad = elem->IsQuadratic();
13025     elemKind.SetQuad( iQuad );
13026
13027     // ------------------------------------------------------------------------------------
13028     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
13029     // ------------------------------------------------------------------------------------
13030     presentBndElems.clear();
13031     missingBndElems.clear();
13032     freeFacets.clear(); nodes.clear(); elemNodes.clear();
13033     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
13034     {
13035       const SMDS_MeshElement* otherVol = 0;
13036       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
13037       {
13038         if ( !vTool.IsFreeFace(iface, &otherVol) &&
13039              ( !aroundElements || elements.count( otherVol )))
13040           continue;
13041         freeFacets.push_back( iface );
13042       }
13043       if ( missType == SMDSAbs_Face )
13044         vTool.SetExternalNormal();
13045       for ( size_t i = 0; i < freeFacets.size(); ++i )
13046       {
13047         int                iface = freeFacets[i];
13048         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
13049         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
13050         if ( missType == SMDSAbs_Edge ) // boundary edges
13051         {
13052           nodes.resize( 2+iQuad );
13053           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
13054           {
13055             for ( size_t j = 0; j < nodes.size(); ++j )
13056               nodes[ j ] = nn[ i+j ];
13057             if ( const SMDS_MeshElement* edge =
13058                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
13059               presentBndElems.push_back( edge );
13060             else
13061               missingBndElems.push_back( nodes );
13062           }
13063         }
13064         else // boundary face
13065         {
13066           nodes.clear();
13067           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13068             nodes.push_back( nn[inode] ); // add corner nodes
13069           if (iQuad)
13070             for ( inode = 1; inode < nbFaceNodes; inode += 2)
13071               nodes.push_back( nn[inode] ); // add medium nodes
13072           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
13073           if ( iCenter > 0 )
13074             nodes.push_back( vTool.GetNodes()[ iCenter ] );
13075
13076           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
13077                                                                SMDSAbs_Face, /*noMedium=*/false ))
13078             presentBndElems.push_back( f );
13079           else
13080             missingBndElems.push_back( nodes );
13081
13082           if ( targetMesh != myMesh )
13083           {
13084             // add 1D elements on face boundary to be added to a new mesh
13085             const SMDS_MeshElement* edge;
13086             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13087             {
13088               if ( iQuad )
13089                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
13090               else
13091                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
13092               if ( edge && avoidSet.insert( edge ).second )
13093                 presentBndElems.push_back( edge );
13094             }
13095           }
13096         }
13097       }
13098     }
13099     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
13100     {
13101       avoidSet.clear(), avoidSet.insert( elem );
13102       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
13103                         SMDS_MeshElement::iterator() );
13104       elemNodes.push_back( elemNodes[0] );
13105       nodes.resize( 2 + iQuad );
13106       const int nbLinks = elem->NbCornerNodes();
13107       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
13108       {
13109         nodes[0] = elemNodes[iN];
13110         nodes[1] = elemNodes[iN+1+iQuad];
13111         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
13112           continue; // not free link
13113
13114         if ( iQuad ) nodes[2] = elemNodes[iN+1];
13115         if ( const SMDS_MeshElement* edge =
13116              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
13117           presentBndElems.push_back( edge );
13118         else
13119           missingBndElems.push_back( nodes );
13120       }
13121     }
13122
13123     // ---------------------------------
13124     // 2. Add missing boundary elements
13125     // ---------------------------------
13126     if ( targetMesh != myMesh )
13127       // instead of making a map of nodes in this mesh and targetMesh,
13128       // we create nodes with same IDs.
13129       for ( size_t i = 0; i < missingBndElems.size(); ++i )
13130       {
13131         TConnectivity& srcNodes = missingBndElems[i];
13132         tgtNodes.resize( srcNodes.size() );
13133         for ( inode = 0; inode < srcNodes.size(); ++inode )
13134           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
13135         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
13136                                                                    missType,
13137                                                                    /*noMedium=*/false))
13138           continue;
13139         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
13140         ++nbAddedBnd;
13141       }
13142     else
13143       for ( size_t i = 0; i < missingBndElems.size(); ++i )
13144       {
13145         TConnectivity& nodes = missingBndElems[ i ];
13146         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
13147                                                                    missType,
13148                                                                    /*noMedium=*/false))
13149           continue;
13150         SMDS_MeshElement* newElem =
13151           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
13152         nbAddedBnd += bool( newElem );
13153
13154         // try to set a new element to a shape
13155         if ( myMesh->HasShapeToMesh() )
13156         {
13157           bool ok = true;
13158           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
13159           const size_t nbN = nodes.size() / (iQuad+1 );
13160           for ( inode = 0; inode < nbN && ok; ++inode )
13161           {
13162             pair<int, TopAbs_ShapeEnum> i_stype =
13163               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
13164             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
13165               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
13166           }
13167           if ( ok && mediumShapes.size() > 1 )
13168           {
13169             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13170             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13171             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13172             {
13173               if (( ok = ( stype_i->first != stype_i_0.first )))
13174                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13175                                         aMesh->IndexToShape( stype_i_0.second ));
13176             }
13177           }
13178           if ( ok && mediumShapes.begin()->first == missShapeType )
13179             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13180         }
13181       }
13182
13183     // ----------------------------------
13184     // 3. Copy present boundary elements
13185     // ----------------------------------
13186     if ( toCopyExistingBoundary )
13187       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13188       {
13189         const SMDS_MeshElement* e = presentBndElems[i];
13190         tgtNodes.resize( e->NbNodes() );
13191         for ( inode = 0; inode < tgtNodes.size(); ++inode )
13192           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13193         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13194       }
13195     else // store present elements to add them to a group
13196       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13197       {
13198         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
13199       }
13200
13201   } // loop on given elements
13202
13203   // ---------------------------------------------
13204   // 4. Fill group with boundary elements
13205   // ---------------------------------------------
13206   if ( group )
13207   {
13208     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13209       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
13210         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
13211   }
13212   tgtEditor.myLastCreatedElems.Clear();
13213   tgtEditor2.myLastCreatedElems.Clear();
13214
13215   // -----------------------
13216   // 5. Copy given elements
13217   // -----------------------
13218   if ( toCopyElements && targetMesh != myMesh )
13219   {
13220     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13221     else                  eIt = elemSetIterator( elements );
13222     while (eIt->more())
13223     {
13224       const SMDS_MeshElement* elem = eIt->next();
13225       tgtNodes.resize( elem->NbNodes() );
13226       for ( inode = 0; inode < tgtNodes.size(); ++inode )
13227         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13228       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13229
13230       tgtEditor.myLastCreatedElems.Clear();
13231     }
13232   }
13233   return nbAddedBnd;
13234 }
13235
13236 //================================================================================
13237 /*!
13238  * \brief Copy node position and set \a to node on the same geometry
13239  */
13240 //================================================================================
13241
13242 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13243                                      const SMDS_MeshNode* to )
13244 {
13245   if ( !from || !to ) return;
13246
13247   SMDS_PositionPtr pos = from->GetPosition();
13248   if ( !pos || from->getshapeId() < 1 ) return;
13249
13250   switch ( pos->GetTypeOfPosition() )
13251   {
13252   case SMDS_TOP_3DSPACE: break;
13253
13254   case SMDS_TOP_FACE:
13255   {
13256     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
13257     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13258                                 fPos->GetUParameter(), fPos->GetVParameter() );
13259     break;
13260   }
13261   case SMDS_TOP_EDGE:
13262   {
13263     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13264     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
13265     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13266     break;
13267   }
13268   case SMDS_TOP_VERTEX:
13269   {
13270     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13271     break;
13272   }
13273   case SMDS_TOP_UNSPEC:
13274   default:;
13275   }
13276 }