Salome HOME
0021542: EDF 1699 SMESH: Reorient a group of faces
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2012  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.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include <Basics_OCCTVersion.hxx>
50
51 #include "utilities.h"
52
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
57 #include <ElCLib.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
73 #include <TopExp.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
78 #include <TopoDS.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
81 #include <gp.hxx>
82 #include <gp_Ax1.hxx>
83 #include <gp_Dir.hxx>
84 #include <gp_Lin.hxx>
85 #include <gp_Pln.hxx>
86 #include <gp_Trsf.hxx>
87 #include <gp_Vec.hxx>
88 #include <gp_XY.hxx>
89 #include <gp_XYZ.hxx>
90
91 #include <cmath>
92
93 #include <map>
94 #include <set>
95 #include <numeric>
96 #include <limits>
97 #include <algorithm>
98 #include <sstream>
99
100 #include <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
102
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104
105 using namespace std;
106 using namespace SMESH::Controls;
107
108 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> >    TElemOfNodeListMap;
109 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
110
111 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
112
113 //=======================================================================
114 //function : SMESH_MeshEditor
115 //purpose  :
116 //=======================================================================
117
118 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
119   :myMesh( theMesh ) // theMesh may be NULL
120 {
121 }
122
123 //================================================================================
124 /*!
125  * \brief Clears myLastCreatedNodes and myLastCreatedElems
126  */
127 //================================================================================
128
129 void SMESH_MeshEditor::CrearLastCreated()
130 {
131   myLastCreatedNodes.Clear();
132   myLastCreatedElems.Clear();
133 }
134
135
136 //=======================================================================
137 /*!
138  * \brief Add element
139  */
140 //=======================================================================
141
142 SMDS_MeshElement*
143 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
144                              const SMDSAbs_ElementType            type,
145                              const bool                           isPoly,
146                              const int                            ID,
147                              const double                         ballDiameter)
148 {
149   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
150   SMDS_MeshElement* e = 0;
151   int nbnode = node.size();
152   SMESHDS_Mesh* mesh = GetMeshDS();
153   switch ( type ) {
154   case SMDSAbs_Face:
155     if ( !isPoly ) {
156       if      (nbnode == 3) {
157         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
158         else           e = mesh->AddFace      (node[0], node[1], node[2] );
159       }
160       else if (nbnode == 4) {
161         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
162         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
163       }
164       else if (nbnode == 6) {
165         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
166                                                node[4], node[5], ID);
167         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
168                                                node[4], node[5] );
169       }
170       else if (nbnode == 8) {
171         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
172                                                node[4], node[5], node[6], node[7], ID);
173         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
174                                                node[4], node[5], node[6], node[7] );
175       }
176       else if (nbnode == 9) {
177         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
178                                                node[4], node[5], node[6], node[7], node[8], ID);
179         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
180                                                node[4], node[5], node[6], node[7], node[8] );
181       }
182     } else {
183       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
184       else           e = mesh->AddPolygonalFace      (node    );
185     }
186     break;
187
188   case SMDSAbs_Volume:
189     if ( !isPoly ) {
190       if      (nbnode == 4) {
191         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
192         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
193       }
194       else if (nbnode == 5) {
195         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
196                                                  node[4], ID);
197         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
198                                                  node[4] );
199       }
200       else if (nbnode == 6) {
201         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
202                                                  node[4], node[5], ID);
203         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
204                                                  node[4], node[5] );
205       }
206       else if (nbnode == 8) {
207         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
208                                                  node[4], node[5], node[6], node[7], ID);
209         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
210                                                  node[4], node[5], node[6], node[7] );
211       }
212       else if (nbnode == 10) {
213         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
214                                                  node[4], node[5], node[6], node[7],
215                                                  node[8], node[9], ID);
216         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
217                                                  node[4], node[5], node[6], node[7],
218                                                  node[8], node[9] );
219       }
220       else if (nbnode == 12) {
221         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
222                                                  node[4], node[5], node[6], node[7],
223                                                  node[8], node[9], node[10], node[11], ID);
224         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
225                                                  node[4], node[5], node[6], node[7],
226                                                  node[8], node[9], node[10], node[11] );
227       }
228       else if (nbnode == 13) {
229         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
230                                                  node[4], node[5], node[6], node[7],
231                                                  node[8], node[9], node[10],node[11],
232                                                  node[12],ID);
233         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
234                                                  node[4], node[5], node[6], node[7],
235                                                  node[8], node[9], node[10],node[11],
236                                                  node[12] );
237       }
238       else if (nbnode == 15) {
239         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240                                                  node[4], node[5], node[6], node[7],
241                                                  node[8], node[9], node[10],node[11],
242                                                  node[12],node[13],node[14],ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
244                                                  node[4], node[5], node[6], node[7],
245                                                  node[8], node[9], node[10],node[11],
246                                                  node[12],node[13],node[14] );
247       }
248       else if (nbnode == 20) {
249         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250                                                  node[4], node[5], node[6], node[7],
251                                                  node[8], node[9], node[10],node[11],
252                                                  node[12],node[13],node[14],node[15],
253                                                  node[16],node[17],node[18],node[19],ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], node[6], node[7],
256                                                  node[8], node[9], node[10],node[11],
257                                                  node[12],node[13],node[14],node[15],
258                                                  node[16],node[17],node[18],node[19] );
259       }
260       else if (nbnode == 27) {
261         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
262                                                  node[4], node[5], node[6], node[7],
263                                                  node[8], node[9], node[10],node[11],
264                                                  node[12],node[13],node[14],node[15],
265                                                  node[16],node[17],node[18],node[19],
266                                                  node[20],node[21],node[22],node[23],
267                                                  node[24],node[25],node[26], ID);
268         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
269                                                  node[4], node[5], node[6], node[7],
270                                                  node[8], node[9], node[10],node[11],
271                                                  node[12],node[13],node[14],node[15],
272                                                  node[16],node[17],node[18],node[19],
273                                                  node[20],node[21],node[22],node[23],
274                                                  node[24],node[25],node[26] );
275       }
276     }
277     break;
278
279   case SMDSAbs_Edge:
280     if ( nbnode == 2 ) {
281       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
282       else           e = mesh->AddEdge      (node[0], node[1] );
283     }
284     else if ( nbnode == 3 ) {
285       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
286       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
287     }
288     break;
289
290   case SMDSAbs_0DElement:
291     if ( nbnode == 1 ) {
292       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
293       else           e = mesh->Add0DElement      (node[0] );
294     }
295     break;
296
297   case SMDSAbs_Node:
298     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
299     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
300     break;
301
302   case SMDSAbs_Ball:
303     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
304     else           e = mesh->AddBall      (node[0], ballDiameter);
305     break;
306
307   default:;
308   }
309   if ( e ) myLastCreatedElems.Append( e );
310   return e;
311 }
312
313 //=======================================================================
314 /*!
315  * \brief Add element
316  */
317 //=======================================================================
318
319 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
320                                                const SMDSAbs_ElementType type,
321                                                const bool                isPoly,
322                                                const int                 ID)
323 {
324   vector<const SMDS_MeshNode*> nodes;
325   nodes.reserve( nodeIDs.size() );
326   vector<int>::const_iterator id = nodeIDs.begin();
327   while ( id != nodeIDs.end() ) {
328     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
329       nodes.push_back( node );
330     else
331       return 0;
332   }
333   return AddElement( nodes, type, isPoly, ID );
334 }
335
336 //=======================================================================
337 //function : Remove
338 //purpose  : Remove a node or an element.
339 //           Modify a compute state of sub-meshes which become empty
340 //=======================================================================
341
342 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
343                               const bool         isNodes )
344 {
345   myLastCreatedElems.Clear();
346   myLastCreatedNodes.Clear();
347
348   SMESHDS_Mesh* aMesh = GetMeshDS();
349   set< SMESH_subMesh *> smmap;
350
351   int removed = 0;
352   list<int>::const_iterator it = theIDs.begin();
353   for ( ; it != theIDs.end(); it++ ) {
354     const SMDS_MeshElement * elem;
355     if ( isNodes )
356       elem = aMesh->FindNode( *it );
357     else
358       elem = aMesh->FindElement( *it );
359     if ( !elem )
360       continue;
361
362     // Notify VERTEX sub-meshes about modification
363     if ( isNodes ) {
364       const SMDS_MeshNode* node = cast2Node( elem );
365       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
366         if ( int aShapeID = node->getshapeId() )
367           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
368             smmap.insert( sm );
369     }
370     // Find sub-meshes to notify about modification
371     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
372     //     while ( nodeIt->more() ) {
373     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
374     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
375     //       if ( aPosition.get() ) {
376     //         if ( int aShapeID = aPosition->GetShapeId() ) {
377     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378     //             smmap.insert( sm );
379     //         }
380     //       }
381     //     }
382
383     // Do remove
384     if ( isNodes )
385       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
386     else
387       aMesh->RemoveElement( elem );
388     removed++;
389   }
390
391   // Notify sub-meshes about modification
392   if ( !smmap.empty() ) {
393     set< SMESH_subMesh *>::iterator smIt;
394     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
395       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
396   }
397
398   //   // Check if the whole mesh becomes empty
399   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
400   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
401
402   return removed;
403 }
404
405 //================================================================================
406 /*!
407  * \brief Create 0D elements on all nodes of the given object except those
408  *        nodes on which a 0D element already exists.
409  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
410  *                    the all mesh is treated
411  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
412  */
413 //================================================================================
414
415 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
416                                                    TIDSortedElemSet&       all0DElems )
417 {
418   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
419   SMDS_ElemIteratorPtr elemIt;
420   if ( elements.empty() )
421     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
422   else
423     elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
424
425   while ( elemIt->more() )
426   {
427     const SMDS_MeshElement* e = elemIt->next();
428     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
429     while ( nodeIt->more() )
430     {
431       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
432       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
433       if ( it0D->more() )
434         all0DElems.insert( it0D->next() );
435       else {
436         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
437         all0DElems.insert( myLastCreatedElems.Last() );
438       }
439     }
440   }
441 }
442
443 //=======================================================================
444 //function : FindShape
445 //purpose  : Return an index of the shape theElem is on
446 //           or zero if a shape not found
447 //=======================================================================
448
449 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
450 {
451   myLastCreatedElems.Clear();
452   myLastCreatedNodes.Clear();
453
454   SMESHDS_Mesh * aMesh = GetMeshDS();
455   if ( aMesh->ShapeToMesh().IsNull() )
456     return 0;
457
458   int aShapeID = theElem->getshapeId();
459   if ( aShapeID < 1 )
460     return 0;
461
462   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
463     if ( sm->Contains( theElem ))
464       return aShapeID;
465
466   if ( theElem->GetType() == SMDSAbs_Node ) {
467     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
468   }
469   else {
470     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
471   }
472
473   TopoDS_Shape aShape; // the shape a node of theElem is on
474   if ( theElem->GetType() != SMDSAbs_Node )
475   {
476     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
477     while ( nodeIt->more() ) {
478       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
479       if ((aShapeID = node->getshapeId()) > 0) {
480         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
481           if ( sm->Contains( theElem ))
482             return aShapeID;
483           if ( aShape.IsNull() )
484             aShape = aMesh->IndexToShape( aShapeID );
485         }
486       }
487     }
488   }
489
490   // None of nodes is on a proper shape,
491   // find the shape among ancestors of aShape on which a node is
492   if ( !aShape.IsNull() ) {
493     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
494     for ( ; ancIt.More(); ancIt.Next() ) {
495       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
496       if ( sm && sm->Contains( theElem ))
497         return aMesh->ShapeToIndex( ancIt.Value() );
498     }
499   }
500   else
501   {
502     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
503     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
504     for ( ; id_sm != id2sm.end(); ++id_sm )
505       if ( id_sm->second->Contains( theElem ))
506         return id_sm->first;
507   }
508
509   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
510   return 0;
511 }
512
513 //=======================================================================
514 //function : IsMedium
515 //purpose  :
516 //=======================================================================
517
518 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
519                                 const SMDSAbs_ElementType typeToCheck)
520 {
521   bool isMedium = false;
522   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
523   while (it->more() && !isMedium ) {
524     const SMDS_MeshElement* elem = it->next();
525     isMedium = elem->IsMediumNode(node);
526   }
527   return isMedium;
528 }
529
530 //=======================================================================
531 //function : ShiftNodesQuadTria
532 //purpose  : auxilary
533 //           Shift nodes in the array corresponded to quadratic triangle
534 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
535 //=======================================================================
536 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
537 {
538   const SMDS_MeshNode* nd1 = aNodes[0];
539   aNodes[0] = aNodes[1];
540   aNodes[1] = aNodes[2];
541   aNodes[2] = nd1;
542   const SMDS_MeshNode* nd2 = aNodes[3];
543   aNodes[3] = aNodes[4];
544   aNodes[4] = aNodes[5];
545   aNodes[5] = nd2;
546 }
547
548 //=======================================================================
549 //function : edgeConnectivity
550 //purpose  : auxilary
551 //           return number of the edges connected with the theNode.
552 //           if theEdges has connections with the other type of the
553 //           elements, return -1
554 //=======================================================================
555 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
556 {
557   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
558   int nb=0;
559   while(elemIt->more()) {
560     elemIt->next();
561     nb++;
562   }
563   return nb;
564 }
565
566
567 //=======================================================================
568 //function : GetNodesFromTwoTria
569 //purpose  : auxilary
570 //           Shift nodes in the array corresponded to quadratic triangle
571 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
572 //=======================================================================
573 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
574                                 const SMDS_MeshElement * theTria2,
575                                 const SMDS_MeshNode* N1[],
576                                 const SMDS_MeshNode* N2[])
577 {
578   SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
579   int i=0;
580   while(i<6) {
581     N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
582     i++;
583   }
584   if(it->more()) return false;
585   it = theTria2->nodesIterator();
586   i=0;
587   while(i<6) {
588     N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
589     i++;
590   }
591   if(it->more()) return false;
592
593   int sames[3] = {-1,-1,-1};
594   int nbsames = 0;
595   int j;
596   for(i=0; i<3; i++) {
597     for(j=0; j<3; j++) {
598       if(N1[i]==N2[j]) {
599         sames[i] = j;
600         nbsames++;
601         break;
602       }
603     }
604   }
605   if(nbsames!=2) return false;
606   if(sames[0]>-1) {
607     ShiftNodesQuadTria(N1);
608     if(sames[1]>-1) {
609       ShiftNodesQuadTria(N1);
610     }
611   }
612   i = sames[0] + sames[1] + sames[2];
613   for(; i<2; i++) {
614     ShiftNodesQuadTria(N2);
615   }
616   // now we receive following N1 and N2 (using numeration as above image)
617   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
618   // i.e. first nodes from both arrays determ new diagonal
619   return true;
620 }
621
622 //=======================================================================
623 //function : InverseDiag
624 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
625 //           but having other common link.
626 //           Return False if args are improper
627 //=======================================================================
628
629 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
630                                     const SMDS_MeshElement * theTria2 )
631 {
632   MESSAGE("InverseDiag");
633   myLastCreatedElems.Clear();
634   myLastCreatedNodes.Clear();
635
636   if (!theTria1 || !theTria2)
637     return false;
638
639   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
640   if (!F1) return false;
641   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
642   if (!F2) return false;
643   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
644       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
645
646     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
647     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
648     //    |/ |                                         | \|
649     //  B +--+ 2                                     B +--+ 2
650
651     // put nodes in array and find out indices of the same ones
652     const SMDS_MeshNode* aNodes [6];
653     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
654     int i = 0;
655     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
656     while ( it->more() ) {
657       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
658
659       if ( i > 2 ) // theTria2
660         // find same node of theTria1
661         for ( int j = 0; j < 3; j++ )
662           if ( aNodes[ i ] == aNodes[ j ]) {
663             sameInd[ j ] = i;
664             sameInd[ i ] = j;
665             break;
666           }
667       // next
668       i++;
669       if ( i == 3 ) {
670         if ( it->more() )
671           return false; // theTria1 is not a triangle
672         it = theTria2->nodesIterator();
673       }
674       if ( i == 6 && it->more() )
675         return false; // theTria2 is not a triangle
676     }
677
678     // find indices of 1,2 and of A,B in theTria1
679     int iA = 0, iB = 0, i1 = 0, i2 = 0;
680     for ( i = 0; i < 6; i++ ) {
681       if ( sameInd [ i ] == 0 ) {
682         if ( i < 3 ) i1 = i;
683         else         i2 = i;
684       }
685       else if (i < 3) {
686         if ( iA ) iB = i;
687         else      iA = i;
688       }
689     }
690     // nodes 1 and 2 should not be the same
691     if ( aNodes[ i1 ] == aNodes[ i2 ] )
692       return false;
693
694     // theTria1: A->2
695     aNodes[ iA ] = aNodes[ i2 ];
696     // theTria2: B->1
697     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
698
699     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
700     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
701
702     return true;
703
704   } // end if(F1 && F2)
705
706   // check case of quadratic faces
707   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
708     return false;
709   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
710     return false;
711
712   //       5
713   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
714   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
715   //    |   / |
716   //  7 +  +  + 6
717   //    | /9  |
718   //    |/    |
719   //  4 +--+--+ 3
720   //       8
721
722   const SMDS_MeshNode* N1 [6];
723   const SMDS_MeshNode* N2 [6];
724   if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
725     return false;
726   // now we receive following N1 and N2 (using numeration as above image)
727   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
728   // i.e. first nodes from both arrays determ new diagonal
729
730   const SMDS_MeshNode* N1new [6];
731   const SMDS_MeshNode* N2new [6];
732   N1new[0] = N1[0];
733   N1new[1] = N2[0];
734   N1new[2] = N2[1];
735   N1new[3] = N1[4];
736   N1new[4] = N2[3];
737   N1new[5] = N1[5];
738   N2new[0] = N1[0];
739   N2new[1] = N1[1];
740   N2new[2] = N2[0];
741   N2new[3] = N1[3];
742   N2new[4] = N2[5];
743   N2new[5] = N1[4];
744   // replaces nodes in faces
745   GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
746   GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
747
748   return true;
749 }
750
751 //=======================================================================
752 //function : findTriangles
753 //purpose  : find triangles sharing theNode1-theNode2 link
754 //=======================================================================
755
756 static bool findTriangles(const SMDS_MeshNode *    theNode1,
757                           const SMDS_MeshNode *    theNode2,
758                           const SMDS_MeshElement*& theTria1,
759                           const SMDS_MeshElement*& theTria2)
760 {
761   if ( !theNode1 || !theNode2 ) return false;
762
763   theTria1 = theTria2 = 0;
764
765   set< const SMDS_MeshElement* > emap;
766   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
767   while (it->more()) {
768     const SMDS_MeshElement* elem = it->next();
769     if ( elem->NbNodes() == 3 )
770       emap.insert( elem );
771   }
772   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
773   while (it->more()) {
774     const SMDS_MeshElement* elem = it->next();
775     if ( emap.find( elem ) != emap.end() ) {
776       if ( theTria1 ) {
777         // theTria1 must be element with minimum ID
778         if( theTria1->GetID() < elem->GetID() ) {
779           theTria2 = elem;
780         }
781         else {
782           theTria2 = theTria1;
783           theTria1 = elem;
784         }
785         break;
786       }
787       else {
788         theTria1 = elem;
789       }
790     }
791   }
792   return ( theTria1 && theTria2 );
793 }
794
795 //=======================================================================
796 //function : InverseDiag
797 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
798 //           with ones built on the same 4 nodes but having other common link.
799 //           Return false if proper faces not found
800 //=======================================================================
801
802 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
803                                     const SMDS_MeshNode * theNode2)
804 {
805   myLastCreatedElems.Clear();
806   myLastCreatedNodes.Clear();
807
808   MESSAGE( "::InverseDiag()" );
809
810   const SMDS_MeshElement *tr1, *tr2;
811   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
812     return false;
813
814   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
815   if (!F1) return false;
816   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
817   if (!F2) return false;
818   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
819       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
820
821     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
822     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
823     //    |/ |                                    | \|
824     //  B +--+ 2                                B +--+ 2
825
826     // put nodes in array
827     // and find indices of 1,2 and of A in tr1 and of B in tr2
828     int i, iA1 = 0, i1 = 0;
829     const SMDS_MeshNode* aNodes1 [3];
830     SMDS_ElemIteratorPtr it;
831     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
832       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
833       if ( aNodes1[ i ] == theNode1 )
834         iA1 = i; // node A in tr1
835       else if ( aNodes1[ i ] != theNode2 )
836         i1 = i;  // node 1
837     }
838     int iB2 = 0, i2 = 0;
839     const SMDS_MeshNode* aNodes2 [3];
840     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
841       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
842       if ( aNodes2[ i ] == theNode2 )
843         iB2 = i; // node B in tr2
844       else if ( aNodes2[ i ] != theNode1 )
845         i2 = i;  // node 2
846     }
847
848     // nodes 1 and 2 should not be the same
849     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
850       return false;
851
852     // tr1: A->2
853     aNodes1[ iA1 ] = aNodes2[ i2 ];
854     // tr2: B->1
855     aNodes2[ iB2 ] = aNodes1[ i1 ];
856
857     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
858     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
859
860     return true;
861   }
862
863   // check case of quadratic faces
864   return InverseDiag(tr1,tr2);
865 }
866
867 //=======================================================================
868 //function : getQuadrangleNodes
869 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
870 //           fusion of triangles tr1 and tr2 having shared link on
871 //           theNode1 and theNode2
872 //=======================================================================
873
874 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
875                         const SMDS_MeshNode *    theNode1,
876                         const SMDS_MeshNode *    theNode2,
877                         const SMDS_MeshElement * tr1,
878                         const SMDS_MeshElement * tr2 )
879 {
880   if( tr1->NbNodes() != tr2->NbNodes() )
881     return false;
882   // find the 4-th node to insert into tr1
883   const SMDS_MeshNode* n4 = 0;
884   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
885   int i=0;
886   while ( !n4 && i<3 ) {
887     const SMDS_MeshNode * n = cast2Node( it->next() );
888     i++;
889     bool isDiag = ( n == theNode1 || n == theNode2 );
890     if ( !isDiag )
891       n4 = n;
892   }
893   // Make an array of nodes to be in a quadrangle
894   int iNode = 0, iFirstDiag = -1;
895   it = tr1->nodesIterator();
896   i=0;
897   while ( i<3 ) {
898     const SMDS_MeshNode * n = cast2Node( it->next() );
899     i++;
900     bool isDiag = ( n == theNode1 || n == theNode2 );
901     if ( isDiag ) {
902       if ( iFirstDiag < 0 )
903         iFirstDiag = iNode;
904       else if ( iNode - iFirstDiag == 1 )
905         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
906     }
907     else if ( n == n4 ) {
908       return false; // tr1 and tr2 should not have all the same nodes
909     }
910     theQuadNodes[ iNode++ ] = n;
911   }
912   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
913     theQuadNodes[ iNode ] = n4;
914
915   return true;
916 }
917
918 //=======================================================================
919 //function : DeleteDiag
920 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
921 //           with a quadrangle built on the same 4 nodes.
922 //           Return false if proper faces not found
923 //=======================================================================
924
925 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
926                                    const SMDS_MeshNode * theNode2)
927 {
928   myLastCreatedElems.Clear();
929   myLastCreatedNodes.Clear();
930
931   MESSAGE( "::DeleteDiag()" );
932
933   const SMDS_MeshElement *tr1, *tr2;
934   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
935     return false;
936
937   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
938   if (!F1) return false;
939   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
940   if (!F2) return false;
941   SMESHDS_Mesh * aMesh = GetMeshDS();
942
943   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
944       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
945
946     const SMDS_MeshNode* aNodes [ 4 ];
947     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
948       return false;
949
950     const SMDS_MeshElement* newElem = 0;
951     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
952     myLastCreatedElems.Append(newElem);
953     AddToSameGroups( newElem, tr1, aMesh );
954     int aShapeId = tr1->getshapeId();
955     if ( aShapeId )
956       {
957         aMesh->SetMeshElementOnShape( newElem, aShapeId );
958       }
959     aMesh->RemoveElement( tr1 );
960     aMesh->RemoveElement( tr2 );
961
962     return true;
963   }
964
965   // check case of quadratic faces
966   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
967     return false;
968   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
969     return false;
970
971   //       5
972   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
973   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
974   //    |   / |
975   //  7 +  +  + 6
976   //    | /9  |
977   //    |/    |
978   //  4 +--+--+ 3
979   //       8
980
981   const SMDS_MeshNode* N1 [6];
982   const SMDS_MeshNode* N2 [6];
983   if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
984     return false;
985   // now we receive following N1 and N2 (using numeration as above image)
986   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
987   // i.e. first nodes from both arrays determ new diagonal
988
989   const SMDS_MeshNode* aNodes[8];
990   aNodes[0] = N1[0];
991   aNodes[1] = N1[1];
992   aNodes[2] = N2[0];
993   aNodes[3] = N2[1];
994   aNodes[4] = N1[3];
995   aNodes[5] = N2[5];
996   aNodes[6] = N2[3];
997   aNodes[7] = N1[5];
998
999   const SMDS_MeshElement* newElem = 0;
1000   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1001                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1002   myLastCreatedElems.Append(newElem);
1003   AddToSameGroups( newElem, tr1, aMesh );
1004   int aShapeId = tr1->getshapeId();
1005   if ( aShapeId )
1006     {
1007       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1008     }
1009   aMesh->RemoveElement( tr1 );
1010   aMesh->RemoveElement( tr2 );
1011
1012   // remove middle node (9)
1013   GetMeshDS()->RemoveNode( N1[4] );
1014
1015   return true;
1016 }
1017
1018 //=======================================================================
1019 //function : Reorient
1020 //purpose  : Reverse theElement orientation
1021 //=======================================================================
1022
1023 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1024 {
1025   MESSAGE("Reorient");
1026   myLastCreatedElems.Clear();
1027   myLastCreatedNodes.Clear();
1028
1029   if (!theElem)
1030     return false;
1031   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1032   if ( !it || !it->more() )
1033     return false;
1034
1035   switch ( theElem->GetType() ) {
1036
1037   case SMDSAbs_Edge:
1038   case SMDSAbs_Face: {
1039     if(!theElem->IsQuadratic()) {
1040       int i = theElem->NbNodes();
1041       vector<const SMDS_MeshNode*> aNodes( i );
1042       while ( it->more() )
1043         aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1044       return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1045     }
1046     else {
1047       // quadratic elements
1048       if(theElem->GetType()==SMDSAbs_Edge) {
1049         vector<const SMDS_MeshNode*> aNodes(3);
1050         aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1051         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1052         aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1053         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1054       }
1055       else {
1056         int nbn = theElem->NbNodes();
1057         vector<const SMDS_MeshNode*> aNodes(nbn);
1058         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1059         int i=1;
1060         for(; i<nbn/2; i++) {
1061           aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1062         }
1063         for(i=0; i<nbn/2; i++) {
1064           aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1065         }
1066         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1067       }
1068     }
1069   }
1070   case SMDSAbs_Volume: {
1071     if (theElem->IsPoly()) {
1072       // TODO reorient vtk polyhedron
1073       MESSAGE("reorient vtk polyhedron ?");
1074       const SMDS_VtkVolume* aPolyedre =
1075         dynamic_cast<const SMDS_VtkVolume*>( theElem );
1076       if (!aPolyedre) {
1077         MESSAGE("Warning: bad volumic element");
1078         return false;
1079       }
1080
1081       int nbFaces = aPolyedre->NbFaces();
1082       vector<const SMDS_MeshNode *> poly_nodes;
1083       vector<int> quantities (nbFaces);
1084
1085       // reverse each face of the polyedre
1086       for (int iface = 1; iface <= nbFaces; iface++) {
1087         int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1088         quantities[iface - 1] = nbFaceNodes;
1089
1090         for (inode = nbFaceNodes; inode >= 1; inode--) {
1091           const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1092           poly_nodes.push_back(curNode);
1093         }
1094       }
1095
1096       return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1097
1098     }
1099     else {
1100       SMDS_VolumeTool vTool;
1101       if ( !vTool.Set( theElem ))
1102         return false;
1103       vTool.Inverse();
1104       MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1105       return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1106     }
1107   }
1108   default:;
1109   }
1110
1111   return false;
1112 }
1113
1114 //================================================================================
1115 /*!
1116  * \brief Reorient faces.
1117  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1118  * \param theDirection - desired direction of normal of \a theFace
1119  * \param theFace - one of \a theFaces that sould be oriented according to
1120  *        \a theDirection and whose orientation defines orientation of other faces
1121  * \return number of reoriented faces.
1122  */
1123 //================================================================================
1124
1125 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1126                                   const gp_Dir&            theDirection,
1127                                   const SMDS_MeshElement * theFace)
1128 {
1129   int nbReori = 0;
1130   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1131
1132   if ( theFaces.empty() )
1133   {
1134     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1135     while ( fIt->more() )
1136       theFaces.insert( theFaces.end(), fIt->next() );
1137   }
1138
1139   // orient theFace according to theDirection
1140   gp_XYZ normal;
1141   SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1142   if ( normal * theDirection.XYZ() < 0 )
1143     nbReori += Reorient( theFace );
1144
1145   // Orient other faces
1146
1147   set< const SMDS_MeshElement* > startFaces;
1148   TIDSortedElemSet avoidSet;
1149   set< SMESH_TLink > checkedLinks;
1150   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1151
1152   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1153     theFaces.erase( theFace );
1154   startFaces.insert( theFace );
1155
1156   int nodeInd1, nodeInd2;
1157   const SMDS_MeshElement*           otherFace;
1158   vector< const SMDS_MeshElement* > facesNearLink;
1159   vector< std::pair< int, int > >   nodeIndsOfFace;
1160
1161   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1162   while ( startFace != startFaces.end() )
1163   {
1164     theFace = *startFace;
1165     const int nbNodes = theFace->NbCornerNodes();
1166
1167     avoidSet.clear();
1168     avoidSet.insert(theFace);
1169
1170     NLink link( theFace->GetNode( 0 ), 0 );
1171     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1172     {
1173       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1174       linkIt_isNew = checkedLinks.insert( link );
1175       if ( !linkIt_isNew.second )
1176       {
1177         // link has already been checked and won't be encountered more
1178         // if the group (theFaces) is manifold
1179         checkedLinks.erase( linkIt_isNew.first );
1180       }
1181       else
1182       {
1183         facesNearLink.clear();
1184         nodeIndsOfFace.clear();
1185         while (( otherFace = FindFaceInSet( link.first, link.second,
1186                                             theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1187           if ( otherFace != theFace)
1188           {
1189             facesNearLink.push_back( otherFace );
1190             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1191             avoidSet.insert( otherFace );
1192           }
1193         if ( facesNearLink.size() > 1 )
1194         {
1195           // select a face most co-directed with theFace,
1196           // other faces won't be visited this time
1197           gp_XYZ NF, NOF;
1198           SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1199           double proj, maxProj = 0;
1200           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1201             SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1202             if (( proj = Abs( NF * NOF )) > maxProj ) {
1203               maxProj = proj;
1204               otherFace = facesNearLink[i];
1205               nodeInd1  = nodeIndsOfFace[i].first;
1206               nodeInd2  = nodeIndsOfFace[i].second;
1207             }
1208           }
1209         }
1210         if ( otherFace && otherFace != theFace)
1211         {
1212           // link must be reverse in otherFace if orientation ot otherFace
1213           // is same as that of theFace
1214           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1215           {
1216             // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1217             //      << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1218             nbReori += Reorient( otherFace );
1219           }
1220           startFaces.insert( otherFace );
1221           if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1222             theFaces.erase( otherFace );
1223         }
1224       }
1225       std::swap( link.first, link.second ); // reverse the link
1226     }
1227     startFaces.erase( startFace );
1228     startFace = startFaces.begin();
1229   }
1230   return nbReori;
1231 }
1232
1233 //=======================================================================
1234 //function : getBadRate
1235 //purpose  :
1236 //=======================================================================
1237
1238 static double getBadRate (const SMDS_MeshElement*               theElem,
1239                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1240 {
1241   SMESH::Controls::TSequenceOfXYZ P;
1242   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1243     return 1e100;
1244   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1245   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1246 }
1247
1248 //=======================================================================
1249 //function : QuadToTri
1250 //purpose  : Cut quadrangles into triangles.
1251 //           theCrit is used to select a diagonal to cut
1252 //=======================================================================
1253
1254 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1255                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1256 {
1257   myLastCreatedElems.Clear();
1258   myLastCreatedNodes.Clear();
1259
1260   MESSAGE( "::QuadToTri()" );
1261
1262   if ( !theCrit.get() )
1263     return false;
1264
1265   SMESHDS_Mesh * aMesh = GetMeshDS();
1266
1267   Handle(Geom_Surface) surface;
1268   SMESH_MesherHelper   helper( *GetMesh() );
1269
1270   TIDSortedElemSet::iterator itElem;
1271   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1272     const SMDS_MeshElement* elem = *itElem;
1273     if ( !elem || elem->GetType() != SMDSAbs_Face )
1274       continue;
1275     if ( elem->NbCornerNodes() != 4 )
1276       continue;
1277
1278     // retrieve element nodes
1279     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1280
1281     // compare two sets of possible triangles
1282     double aBadRate1, aBadRate2; // to what extent a set is bad
1283     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1284     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1285     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1286
1287     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1288     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1289     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1290
1291     int aShapeId = FindShape( elem );
1292     const SMDS_MeshElement* newElem1 = 0;
1293     const SMDS_MeshElement* newElem2 = 0;
1294
1295     if( !elem->IsQuadratic() ) {
1296
1297       // split liner quadrangle
1298       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1299       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1300       if ( aBadRate1 <= aBadRate2 ) {
1301         // tr1 + tr2 is better
1302         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1303         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1304       }
1305       else {
1306         // tr3 + tr4 is better
1307         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1308         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1309       }
1310     }
1311     else {
1312
1313       // split quadratic quadrangle
1314
1315       // get surface elem is on
1316       if ( aShapeId != helper.GetSubShapeID() ) {
1317         surface.Nullify();
1318         TopoDS_Shape shape;
1319         if ( aShapeId > 0 )
1320           shape = aMesh->IndexToShape( aShapeId );
1321         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1322           TopoDS_Face face = TopoDS::Face( shape );
1323           surface = BRep_Tool::Surface( face );
1324           if ( !surface.IsNull() )
1325             helper.SetSubShape( shape );
1326         }
1327       }
1328       // find middle point for (0,1,2,3)
1329       // and create a node in this point;
1330       const SMDS_MeshNode* newN = 0;
1331       if ( aNodes.size() == 9 )
1332       {
1333         // SMDSEntity_BiQuad_Quadrangle
1334         newN = aNodes.back();
1335       }
1336       else
1337       {
1338         gp_XYZ p( 0,0,0 );
1339         if ( surface.IsNull() )
1340         {
1341           for ( int i = 0; i < 4; i++ )
1342             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1343           p /= 4;
1344         }
1345         else
1346         {
1347           const SMDS_MeshNode* inFaceNode = 0;
1348           if ( helper.GetNodeUVneedInFaceNode() )
1349             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1350               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1351                 inFaceNode = aNodes[ i ];
1352
1353           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1354           gp_XY uv( 0,0 );
1355           for ( int i = 0; i < 4; i++ )
1356             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1357           uv /= 4.;
1358           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1359         }
1360         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1361         myLastCreatedNodes.Append(newN);
1362       }
1363       // create a new element
1364       if ( aBadRate1 <= aBadRate2 ) {
1365         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1366                                   aNodes[6], aNodes[7], newN );
1367         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1368                                   newN,      aNodes[4], aNodes[5] );
1369       }
1370       else {
1371         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1372                                   aNodes[7], aNodes[4], newN );
1373         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1374                                   newN,      aNodes[5], aNodes[6] );
1375       }
1376     } // quadratic case
1377
1378     // care of a new element
1379
1380     myLastCreatedElems.Append(newElem1);
1381     myLastCreatedElems.Append(newElem2);
1382     AddToSameGroups( newElem1, elem, aMesh );
1383     AddToSameGroups( newElem2, elem, aMesh );
1384
1385     // put a new triangle on the same shape
1386     if ( aShapeId )
1387       {
1388         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1389         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1390       }
1391     aMesh->RemoveElement( elem );
1392   }
1393   return true;
1394 }
1395
1396 //=======================================================================
1397 //function : BestSplit
1398 //purpose  : Find better diagonal for cutting.
1399 //=======================================================================
1400
1401 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1402                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1403 {
1404   myLastCreatedElems.Clear();
1405   myLastCreatedNodes.Clear();
1406
1407   if (!theCrit.get())
1408     return -1;
1409
1410   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1411     return -1;
1412
1413   if( theQuad->NbNodes()==4 ||
1414       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1415
1416     // retrieve element nodes
1417     const SMDS_MeshNode* aNodes [4];
1418     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1419     int i = 0;
1420     //while (itN->more())
1421     while (i<4) {
1422       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1423     }
1424     // compare two sets of possible triangles
1425     double aBadRate1, aBadRate2; // to what extent a set is bad
1426     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1427     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1428     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1429
1430     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1431     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1432     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1433     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1434     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1435     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1436       return 1; // diagonal 1-3
1437
1438     return 2; // diagonal 2-4
1439   }
1440   return -1;
1441 }
1442
1443 namespace
1444 {
1445   // Methods of splitting volumes into tetra
1446
1447   const int theHexTo5_1[5*4+1] =
1448     {
1449       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1450     };
1451   const int theHexTo5_2[5*4+1] =
1452     {
1453       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1454     };
1455   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1456
1457   const int theHexTo6_1[6*4+1] =
1458     {
1459       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
1460     };
1461   const int theHexTo6_2[6*4+1] =
1462     {
1463       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
1464     };
1465   const int theHexTo6_3[6*4+1] =
1466     {
1467       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
1468     };
1469   const int theHexTo6_4[6*4+1] =
1470     {
1471       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
1472     };
1473   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1474
1475   const int thePyraTo2_1[2*4+1] =
1476     {
1477       0, 1, 2, 4,    0, 2, 3, 4,   -1
1478     };
1479   const int thePyraTo2_2[2*4+1] =
1480     {
1481       1, 2, 3, 4,    1, 3, 0, 4,   -1
1482     };
1483   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1484
1485   const int thePentaTo3_1[3*4+1] =
1486     {
1487       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1488     };
1489   const int thePentaTo3_2[3*4+1] =
1490     {
1491       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1492     };
1493   const int thePentaTo3_3[3*4+1] =
1494     {
1495       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1496     };
1497   const int thePentaTo3_4[3*4+1] =
1498     {
1499       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1500     };
1501   const int thePentaTo3_5[3*4+1] =
1502     {
1503       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1504     };
1505   const int thePentaTo3_6[3*4+1] =
1506     {
1507       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1508     };
1509   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1510                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1511
1512   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1513   {
1514     int _n1, _n2, _n3;
1515     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1516     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1517     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1518   };
1519   struct TSplitMethod
1520   {
1521     int        _nbTetra;
1522     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1523     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1524     bool       _ownConn;      //!< to delete _connectivity in destructor
1525     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1526
1527     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1528       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1529     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1530     bool hasFacet( const TTriangleFacet& facet ) const
1531     {
1532       const int* tetConn = _connectivity;
1533       for ( ; tetConn[0] >= 0; tetConn += 4 )
1534         if (( facet.contains( tetConn[0] ) +
1535               facet.contains( tetConn[1] ) +
1536               facet.contains( tetConn[2] ) +
1537               facet.contains( tetConn[3] )) == 3 )
1538           return true;
1539       return false;
1540     }
1541   };
1542
1543   //=======================================================================
1544   /*!
1545    * \brief return TSplitMethod for the given element
1546    */
1547   //=======================================================================
1548
1549   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1550   {
1551     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1552
1553     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1554     // an edge and a face barycenter; tertaherdons are based on triangles and
1555     // a volume barycenter
1556     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1557
1558     // Find out how adjacent volumes are split
1559
1560     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1561     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1562     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1563     {
1564       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1565       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1566       if ( nbNodes < 4 ) continue;
1567
1568       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1569       const int* nInd = vol.GetFaceNodesIndices( iF );
1570       if ( nbNodes == 4 )
1571       {
1572         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1573         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1574         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1575         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1576       }
1577       else
1578       {
1579         int iCom = 0; // common node of triangle faces to split into
1580         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1581         {
1582           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1583                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1584                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1585           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1586                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1587                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1588           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1589           {
1590             triaSplits.push_back( t012 );
1591             triaSplits.push_back( t023 );
1592             break;
1593           }
1594         }
1595       }
1596       if ( !triaSplits.empty() )
1597         hasAdjacentSplits = true;
1598     }
1599
1600     // Among variants of split method select one compliant with adjacent volumes
1601
1602     TSplitMethod method;
1603     if ( !vol.Element()->IsPoly() && !is24TetMode )
1604     {
1605       int nbVariants = 2, nbTet = 0;
1606       const int** connVariants = 0;
1607       switch ( vol.Element()->GetEntityType() )
1608       {
1609       case SMDSEntity_Hexa:
1610       case SMDSEntity_Quad_Hexa:
1611       case SMDSEntity_TriQuad_Hexa:
1612         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1613           connVariants = theHexTo5, nbTet = 5;
1614         else
1615           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1616         break;
1617       case SMDSEntity_Pyramid:
1618       case SMDSEntity_Quad_Pyramid:
1619         connVariants = thePyraTo2;  nbTet = 2;
1620         break;
1621       case SMDSEntity_Penta:
1622       case SMDSEntity_Quad_Penta:
1623         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1624         break;
1625       default:
1626         nbVariants = 0;
1627       }
1628       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1629       {
1630         // check method compliancy with adjacent tetras,
1631         // all found splits must be among facets of tetras described by this method
1632         method = TSplitMethod( nbTet, connVariants[variant] );
1633         if ( hasAdjacentSplits && method._nbTetra > 0 )
1634         {
1635           bool facetCreated = true;
1636           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1637           {
1638             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1639             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1640               facetCreated = method.hasFacet( *facet );
1641           }
1642           if ( !facetCreated )
1643             method = TSplitMethod(0); // incompatible method
1644         }
1645       }
1646     }
1647     if ( method._nbTetra < 1 )
1648     {
1649       // No standard method is applicable, use a generic solution:
1650       // each facet of a volume is split into triangles and
1651       // each of triangles and a volume barycenter form a tetrahedron.
1652
1653       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1654
1655       int* connectivity = new int[ maxTetConnSize + 1 ];
1656       method._connectivity = connectivity;
1657       method._ownConn = true;
1658       method._baryNode = !isHex27; // to create central node or not
1659
1660       int connSize = 0;
1661       int baryCenInd = vol.NbNodes() - int( isHex27 );
1662       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1663       {
1664         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1665         const int*   nInd = vol.GetFaceNodesIndices( iF );
1666         // find common node of triangle facets of tetra to create
1667         int iCommon = 0; // index in linear numeration
1668         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1669         if ( !triaSplits.empty() )
1670         {
1671           // by found facets
1672           const TTriangleFacet* facet = &triaSplits.front();
1673           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1674             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1675                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1676               break;
1677         }
1678         else if ( nbNodes > 3 && !is24TetMode )
1679         {
1680           // find the best method of splitting into triangles by aspect ratio
1681           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1682           map< double, int > badness2iCommon;
1683           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1684           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1685           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1686           {
1687             double badness = 0;
1688             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1689             {
1690               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1691                                       nodes[ iQ*((iLast-1)%nbNodes)],
1692                                       nodes[ iQ*((iLast  )%nbNodes)]);
1693               badness += getBadRate( &tria, aspectRatio );
1694             }
1695             badness2iCommon.insert( make_pair( badness, iCommon ));
1696           }
1697           // use iCommon with lowest badness
1698           iCommon = badness2iCommon.begin()->second;
1699         }
1700         if ( iCommon >= nbNodes )
1701           iCommon = 0; // something wrong
1702
1703         // fill connectivity of tetrahedra based on a current face
1704         int nbTet = nbNodes - 2;
1705         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1706         {
1707           int faceBaryCenInd;
1708           if ( isHex27 )
1709           {
1710             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1711             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1712           }
1713           else
1714           {
1715             method._faceBaryNode[ iF ] = 0;
1716             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1717           }
1718           nbTet = nbNodes;
1719           for ( int i = 0; i < nbTet; ++i )
1720           {
1721             int i1 = i, i2 = (i+1) % nbNodes;
1722             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1723             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1724             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1725             connectivity[ connSize++ ] = faceBaryCenInd;
1726             connectivity[ connSize++ ] = baryCenInd;
1727           }
1728         }
1729         else
1730         {
1731           for ( int i = 0; i < nbTet; ++i )
1732           {
1733             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1734             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1735             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1736             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1737             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1738             connectivity[ connSize++ ] = baryCenInd;
1739           }
1740         }
1741         method._nbTetra += nbTet;
1742
1743       } // loop on volume faces
1744
1745       connectivity[ connSize++ ] = -1;
1746
1747     } // end of generic solution
1748
1749     return method;
1750   }
1751   //================================================================================
1752   /*!
1753    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1754    */
1755   //================================================================================
1756
1757   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1758   {
1759     // find the tetrahedron including the three nodes of facet
1760     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1761     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1762     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1763     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1764     while ( volIt1->more() )
1765     {
1766       const SMDS_MeshElement* v = volIt1->next();
1767       SMDSAbs_EntityType type = v->GetEntityType();
1768       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1769         continue;
1770       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1771         continue; // medium node not allowed
1772       const int ind2 = v->GetNodeIndex( n2 );
1773       if ( ind2 < 0 || 3 < ind2 )
1774         continue;
1775       const int ind3 = v->GetNodeIndex( n3 );
1776       if ( ind3 < 0 || 3 < ind3 )
1777         continue;
1778       return true;
1779     }
1780     return false;
1781   }
1782
1783   //=======================================================================
1784   /*!
1785    * \brief A key of a face of volume
1786    */
1787   //=======================================================================
1788
1789   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1790   {
1791     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1792     {
1793       TIDSortedNodeSet sortedNodes;
1794       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1795       int nbNodes = vol.NbFaceNodes( iF );
1796       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1797       for ( int i = 0; i < nbNodes; i += iQ )
1798         sortedNodes.insert( fNodes[i] );
1799       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1800       first.first   = (*(n++))->GetID();
1801       first.second  = (*(n++))->GetID();
1802       second.first  = (*(n++))->GetID();
1803       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1804     }
1805   };
1806 } // namespace
1807
1808 //=======================================================================
1809 //function : SplitVolumesIntoTetra
1810 //purpose  : Split volume elements into tetrahedra.
1811 //=======================================================================
1812
1813 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1814                                               const int                theMethodFlags)
1815 {
1816   // std-like iterator on coordinates of nodes of mesh element
1817   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1818   NXyzIterator xyzEnd;
1819
1820   SMDS_VolumeTool    volTool;
1821   SMESH_MesherHelper helper( *GetMesh());
1822
1823   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1824   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1825
1826   SMESH_SequenceOfElemPtr newNodes, newElems;
1827
1828   // map face of volume to it's baricenrtic node
1829   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1830   double bc[3];
1831
1832   TIDSortedElemSet::const_iterator elem = theElems.begin();
1833   for ( ; elem != theElems.end(); ++elem )
1834   {
1835     if ( (*elem)->GetType() != SMDSAbs_Volume )
1836       continue;
1837     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1838     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1839       continue;
1840
1841     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1842
1843     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1844     if ( splitMethod._nbTetra < 1 ) continue;
1845
1846     // find submesh to add new tetras to
1847     if ( !subMesh || !subMesh->Contains( *elem ))
1848     {
1849       int shapeID = FindShape( *elem );
1850       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1851       subMesh = GetMeshDS()->MeshElements( shapeID );
1852     }
1853     int iQ;
1854     if ( (*elem)->IsQuadratic() )
1855     {
1856       iQ = 2;
1857       // add quadratic links to the helper
1858       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1859       {
1860         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1861         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1862         for ( int iN = 0; iN < nbN; iN += iQ )
1863           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1864       }
1865       helper.SetIsQuadratic( true );
1866     }
1867     else
1868     {
1869       iQ = 1;
1870       helper.SetIsQuadratic( false );
1871     }
1872     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1873     helper.SetElementsOnShape( true );
1874     if ( splitMethod._baryNode )
1875     {
1876       // make a node at barycenter
1877       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1878       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1879       nodes.push_back( gcNode );
1880       newNodes.Append( gcNode );
1881     }
1882     if ( !splitMethod._faceBaryNode.empty() )
1883     {
1884       // make or find baricentric nodes of faces
1885       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1886       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1887       {
1888         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1889           volFace2BaryNode.insert
1890           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1891         if ( !f_n->second )
1892         {
1893           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1894           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1895         }
1896         nodes.push_back( iF_n->second = f_n->second );
1897       }
1898     }
1899
1900     // make tetras
1901     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1902     const int* tetConn = splitMethod._connectivity;
1903     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1904       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1905                                                        nodes[ tetConn[1] ],
1906                                                        nodes[ tetConn[2] ],
1907                                                        nodes[ tetConn[3] ]));
1908
1909     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1910
1911     // Split faces on sides of the split volume
1912
1913     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1914     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1915     {
1916       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1917       if ( nbNodes < 4 ) continue;
1918
1919       // find an existing face
1920       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1921                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1922       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1923                                                                        /*noMedium=*/false))
1924       {
1925         // make triangles
1926         helper.SetElementsOnShape( false );
1927         vector< const SMDS_MeshElement* > triangles;
1928
1929         // find submesh to add new triangles in
1930         if ( !fSubMesh || !fSubMesh->Contains( face ))
1931         {
1932           int shapeID = FindShape( face );
1933           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1934         }
1935         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1936         if ( iF_n != splitMethod._faceBaryNode.end() )
1937         {
1938           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1939           {
1940             const SMDS_MeshNode* n1 = fNodes[iN];
1941             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1942             const SMDS_MeshNode *n3 = iF_n->second;
1943             if ( !volTool.IsFaceExternal( iF ))
1944               swap( n2, n3 );
1945             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1946
1947             if ( fSubMesh && n3->getshapeId() < 1 )
1948               fSubMesh->AddNode( n3 );
1949           }
1950         }
1951         else
1952         {
1953           // among possible triangles create ones discribed by split method
1954           const int* nInd = volTool.GetFaceNodesIndices( iF );
1955           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1956           int iCom = 0; // common node of triangle faces to split into
1957           list< TTriangleFacet > facets;
1958           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1959           {
1960             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1961                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1962                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1963             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1964                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1965                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1966             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1967             {
1968               facets.push_back( t012 );
1969               facets.push_back( t023 );
1970               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1971                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1972                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1973                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1974               break;
1975             }
1976           }
1977           list< TTriangleFacet >::iterator facet = facets.begin();
1978           for ( ; facet != facets.end(); ++facet )
1979           {
1980             if ( !volTool.IsFaceExternal( iF ))
1981               swap( facet->_n2, facet->_n3 );
1982             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1983                                                  volNodes[ facet->_n2 ],
1984                                                  volNodes[ facet->_n3 ]));
1985           }
1986         }
1987         for ( int i = 0; i < triangles.size(); ++i )
1988         {
1989           if ( !triangles[i] ) continue;
1990           if ( fSubMesh )
1991             fSubMesh->AddElement( triangles[i]);
1992           newElems.Append( triangles[i] );
1993         }
1994         ReplaceElemInGroups( face, triangles, GetMeshDS() );
1995         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1996       }
1997
1998     } // loop on volume faces to split them into triangles
1999
2000     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2001
2002     if ( geomType == SMDSEntity_TriQuad_Hexa )
2003     {
2004       // remove medium nodes that could become free
2005       for ( int i = 20; i < volTool.NbNodes(); ++i )
2006         if ( volNodes[i]->NbInverseElements() == 0 )
2007           GetMeshDS()->RemoveNode( volNodes[i] );
2008     }
2009   } // loop on volumes to split
2010
2011   myLastCreatedNodes = newNodes;
2012   myLastCreatedElems = newElems;
2013 }
2014
2015 //=======================================================================
2016 //function : AddToSameGroups
2017 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2018 //=======================================================================
2019
2020 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2021                                         const SMDS_MeshElement* elemInGroups,
2022                                         SMESHDS_Mesh *          aMesh)
2023 {
2024   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2025   if (!groups.empty()) {
2026     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2027     for ( ; grIt != groups.end(); grIt++ ) {
2028       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2029       if ( group && group->Contains( elemInGroups ))
2030         group->SMDSGroup().Add( elemToAdd );
2031     }
2032   }
2033 }
2034
2035
2036 //=======================================================================
2037 //function : RemoveElemFromGroups
2038 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2039 //=======================================================================
2040 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2041                                              SMESHDS_Mesh *          aMesh)
2042 {
2043   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2044   if (!groups.empty())
2045   {
2046     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2047     for (; GrIt != groups.end(); GrIt++)
2048     {
2049       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2050       if (!grp || grp->IsEmpty()) continue;
2051       grp->SMDSGroup().Remove(removeelem);
2052     }
2053   }
2054 }
2055
2056 //================================================================================
2057 /*!
2058  * \brief Replace elemToRm by elemToAdd in the all groups
2059  */
2060 //================================================================================
2061
2062 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2063                                             const SMDS_MeshElement* elemToAdd,
2064                                             SMESHDS_Mesh *          aMesh)
2065 {
2066   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2067   if (!groups.empty()) {
2068     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2069     for ( ; grIt != groups.end(); grIt++ ) {
2070       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2071       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2072         group->SMDSGroup().Add( elemToAdd );
2073     }
2074   }
2075 }
2076
2077 //================================================================================
2078 /*!
2079  * \brief Replace elemToRm by elemToAdd in the all groups
2080  */
2081 //================================================================================
2082
2083 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2084                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2085                                             SMESHDS_Mesh *                         aMesh)
2086 {
2087   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2088   if (!groups.empty())
2089   {
2090     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2091     for ( ; grIt != groups.end(); grIt++ ) {
2092       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2093       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2094         for ( int i = 0; i < elemToAdd.size(); ++i )
2095           group->SMDSGroup().Add( elemToAdd[ i ] );
2096     }
2097   }
2098 }
2099
2100 //=======================================================================
2101 //function : QuadToTri
2102 //purpose  : Cut quadrangles into triangles.
2103 //           theCrit is used to select a diagonal to cut
2104 //=======================================================================
2105
2106 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2107                                   const bool         the13Diag)
2108 {
2109   myLastCreatedElems.Clear();
2110   myLastCreatedNodes.Clear();
2111
2112   MESSAGE( "::QuadToTri()" );
2113
2114   SMESHDS_Mesh * aMesh = GetMeshDS();
2115
2116   Handle(Geom_Surface) surface;
2117   SMESH_MesherHelper   helper( *GetMesh() );
2118
2119   TIDSortedElemSet::iterator itElem;
2120   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2121     const SMDS_MeshElement* elem = *itElem;
2122     if ( !elem || elem->GetType() != SMDSAbs_Face )
2123       continue;
2124     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2125     if(!isquad) continue;
2126
2127     if(elem->NbNodes()==4) {
2128       // retrieve element nodes
2129       const SMDS_MeshNode* aNodes [4];
2130       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2131       int i = 0;
2132       while ( itN->more() )
2133         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2134
2135       int aShapeId = FindShape( elem );
2136       const SMDS_MeshElement* newElem1 = 0;
2137       const SMDS_MeshElement* newElem2 = 0;
2138       if ( the13Diag ) {
2139         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2140         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2141       }
2142       else {
2143         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2144         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2145       }
2146       myLastCreatedElems.Append(newElem1);
2147       myLastCreatedElems.Append(newElem2);
2148       // put a new triangle on the same shape and add to the same groups
2149       if ( aShapeId )
2150         {
2151           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2152           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2153         }
2154       AddToSameGroups( newElem1, elem, aMesh );
2155       AddToSameGroups( newElem2, elem, aMesh );
2156       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2157       aMesh->RemoveElement( elem );
2158     }
2159
2160     // Quadratic quadrangle
2161
2162     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2163
2164       // get surface elem is on
2165       int aShapeId = FindShape( elem );
2166       if ( aShapeId != helper.GetSubShapeID() ) {
2167         surface.Nullify();
2168         TopoDS_Shape shape;
2169         if ( aShapeId > 0 )
2170           shape = aMesh->IndexToShape( aShapeId );
2171         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2172           TopoDS_Face face = TopoDS::Face( shape );
2173           surface = BRep_Tool::Surface( face );
2174           if ( !surface.IsNull() )
2175             helper.SetSubShape( shape );
2176         }
2177       }
2178
2179       const SMDS_MeshNode* aNodes [8];
2180       const SMDS_MeshNode* inFaceNode = 0;
2181       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2182       int i = 0;
2183       while ( itN->more() ) {
2184         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2185         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2186              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2187         {
2188           inFaceNode = aNodes[ i-1 ];
2189         }
2190       }
2191
2192       // find middle point for (0,1,2,3)
2193       // and create a node in this point;
2194       gp_XYZ p( 0,0,0 );
2195       if ( surface.IsNull() ) {
2196         for(i=0; i<4; i++)
2197           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2198         p /= 4;
2199       }
2200       else {
2201         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2202         gp_XY uv( 0,0 );
2203         for(i=0; i<4; i++)
2204           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2205         uv /= 4.;
2206         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2207       }
2208       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2209       myLastCreatedNodes.Append(newN);
2210
2211       // create a new element
2212       const SMDS_MeshElement* newElem1 = 0;
2213       const SMDS_MeshElement* newElem2 = 0;
2214       if ( the13Diag ) {
2215         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2216                                   aNodes[6], aNodes[7], newN );
2217         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2218                                   newN,      aNodes[4], aNodes[5] );
2219       }
2220       else {
2221         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2222                                   aNodes[7], aNodes[4], newN );
2223         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2224                                   newN,      aNodes[5], aNodes[6] );
2225       }
2226       myLastCreatedElems.Append(newElem1);
2227       myLastCreatedElems.Append(newElem2);
2228       // put a new triangle on the same shape and add to the same groups
2229       if ( aShapeId )
2230         {
2231           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2232           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2233         }
2234       AddToSameGroups( newElem1, elem, aMesh );
2235       AddToSameGroups( newElem2, elem, aMesh );
2236       aMesh->RemoveElement( elem );
2237     }
2238   }
2239
2240   return true;
2241 }
2242
2243 //=======================================================================
2244 //function : getAngle
2245 //purpose  :
2246 //=======================================================================
2247
2248 double getAngle(const SMDS_MeshElement * tr1,
2249                 const SMDS_MeshElement * tr2,
2250                 const SMDS_MeshNode *    n1,
2251                 const SMDS_MeshNode *    n2)
2252 {
2253   double angle = 2. * M_PI; // bad angle
2254
2255   // get normals
2256   SMESH::Controls::TSequenceOfXYZ P1, P2;
2257   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2258        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2259     return angle;
2260   gp_Vec N1,N2;
2261   if(!tr1->IsQuadratic())
2262     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2263   else
2264     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2265   if ( N1.SquareMagnitude() <= gp::Resolution() )
2266     return angle;
2267   if(!tr2->IsQuadratic())
2268     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2269   else
2270     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2271   if ( N2.SquareMagnitude() <= gp::Resolution() )
2272     return angle;
2273
2274   // find the first diagonal node n1 in the triangles:
2275   // take in account a diagonal link orientation
2276   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2277   for ( int t = 0; t < 2; t++ ) {
2278     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2279     int i = 0, iDiag = -1;
2280     while ( it->more()) {
2281       const SMDS_MeshElement *n = it->next();
2282       if ( n == n1 || n == n2 ) {
2283         if ( iDiag < 0)
2284           iDiag = i;
2285         else {
2286           if ( i - iDiag == 1 )
2287             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2288           else
2289             nFirst[ t ] = n;
2290           break;
2291         }
2292       }
2293       i++;
2294     }
2295   }
2296   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2297     N2.Reverse();
2298
2299   angle = N1.Angle( N2 );
2300   //SCRUTE( angle );
2301   return angle;
2302 }
2303
2304 // =================================================
2305 // class generating a unique ID for a pair of nodes
2306 // and able to return nodes by that ID
2307 // =================================================
2308 class LinkID_Gen {
2309 public:
2310
2311   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2312     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2313   {}
2314
2315   long GetLinkID (const SMDS_MeshNode * n1,
2316                   const SMDS_MeshNode * n2) const
2317   {
2318     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2319   }
2320
2321   bool GetNodes (const long             theLinkID,
2322                  const SMDS_MeshNode* & theNode1,
2323                  const SMDS_MeshNode* & theNode2) const
2324   {
2325     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2326     if ( !theNode1 ) return false;
2327     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2328     if ( !theNode2 ) return false;
2329     return true;
2330   }
2331
2332 private:
2333   LinkID_Gen();
2334   const SMESHDS_Mesh* myMesh;
2335   long                myMaxID;
2336 };
2337
2338
2339 //=======================================================================
2340 //function : TriToQuad
2341 //purpose  : Fuse neighbour triangles into quadrangles.
2342 //           theCrit is used to select a neighbour to fuse with.
2343 //           theMaxAngle is a max angle between element normals at which
2344 //           fusion is still performed.
2345 //=======================================================================
2346
2347 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2348                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2349                                   const double                         theMaxAngle)
2350 {
2351   myLastCreatedElems.Clear();
2352   myLastCreatedNodes.Clear();
2353
2354   MESSAGE( "::TriToQuad()" );
2355
2356   if ( !theCrit.get() )
2357     return false;
2358
2359   SMESHDS_Mesh * aMesh = GetMeshDS();
2360
2361   // Prepare data for algo: build
2362   // 1. map of elements with their linkIDs
2363   // 2. map of linkIDs with their elements
2364
2365   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2366   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2367   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2368   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2369
2370   TIDSortedElemSet::iterator itElem;
2371   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2372     const SMDS_MeshElement* elem = *itElem;
2373     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2374     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2375     if(!IsTria) continue;
2376
2377     // retrieve element nodes
2378     const SMDS_MeshNode* aNodes [4];
2379     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2380     int i = 0;
2381     while ( i<3 )
2382       aNodes[ i++ ] = cast2Node( itN->next() );
2383     aNodes[ 3 ] = aNodes[ 0 ];
2384
2385     // fill maps
2386     for ( i = 0; i < 3; i++ ) {
2387       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2388       // check if elements sharing a link can be fused
2389       itLE = mapLi_listEl.find( link );
2390       if ( itLE != mapLi_listEl.end() ) {
2391         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2392           continue;
2393         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2394         //if ( FindShape( elem ) != FindShape( elem2 ))
2395         //  continue; // do not fuse triangles laying on different shapes
2396         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2397           continue; // avoid making badly shaped quads
2398         (*itLE).second.push_back( elem );
2399       }
2400       else {
2401         mapLi_listEl[ link ].push_back( elem );
2402       }
2403       mapEl_setLi [ elem ].insert( link );
2404     }
2405   }
2406   // Clean the maps from the links shared by a sole element, ie
2407   // links to which only one element is bound in mapLi_listEl
2408
2409   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2410     int nbElems = (*itLE).second.size();
2411     if ( nbElems < 2  ) {
2412       const SMDS_MeshElement* elem = (*itLE).second.front();
2413       SMESH_TLink link = (*itLE).first;
2414       mapEl_setLi[ elem ].erase( link );
2415       if ( mapEl_setLi[ elem ].empty() )
2416         mapEl_setLi.erase( elem );
2417     }
2418   }
2419
2420   // Algo: fuse triangles into quadrangles
2421
2422   while ( ! mapEl_setLi.empty() ) {
2423     // Look for the start element:
2424     // the element having the least nb of shared links
2425     const SMDS_MeshElement* startElem = 0;
2426     int minNbLinks = 4;
2427     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2428       int nbLinks = (*itEL).second.size();
2429       if ( nbLinks < minNbLinks ) {
2430         startElem = (*itEL).first;
2431         minNbLinks = nbLinks;
2432         if ( minNbLinks == 1 )
2433           break;
2434       }
2435     }
2436
2437     // search elements to fuse starting from startElem or links of elements
2438     // fused earlyer - startLinks
2439     list< SMESH_TLink > startLinks;
2440     while ( startElem || !startLinks.empty() ) {
2441       while ( !startElem && !startLinks.empty() ) {
2442         // Get an element to start, by a link
2443         SMESH_TLink linkId = startLinks.front();
2444         startLinks.pop_front();
2445         itLE = mapLi_listEl.find( linkId );
2446         if ( itLE != mapLi_listEl.end() ) {
2447           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2448           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2449           for ( ; itE != listElem.end() ; itE++ )
2450             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2451               startElem = (*itE);
2452           mapLi_listEl.erase( itLE );
2453         }
2454       }
2455
2456       if ( startElem ) {
2457         // Get candidates to be fused
2458         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2459         const SMESH_TLink *link12, *link13;
2460         startElem = 0;
2461         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2462         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2463         ASSERT( !setLi.empty() );
2464         set< SMESH_TLink >::iterator itLi;
2465         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2466         {
2467           const SMESH_TLink & link = (*itLi);
2468           itLE = mapLi_listEl.find( link );
2469           if ( itLE == mapLi_listEl.end() )
2470             continue;
2471
2472           const SMDS_MeshElement* elem = (*itLE).second.front();
2473           if ( elem == tr1 )
2474             elem = (*itLE).second.back();
2475           mapLi_listEl.erase( itLE );
2476           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2477             continue;
2478           if ( tr2 ) {
2479             tr3 = elem;
2480             link13 = &link;
2481           }
2482           else {
2483             tr2 = elem;
2484             link12 = &link;
2485           }
2486
2487           // add other links of elem to list of links to re-start from
2488           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2489           set< SMESH_TLink >::iterator it;
2490           for ( it = links.begin(); it != links.end(); it++ ) {
2491             const SMESH_TLink& link2 = (*it);
2492             if ( link2 != link )
2493               startLinks.push_back( link2 );
2494           }
2495         }
2496
2497         // Get nodes of possible quadrangles
2498         const SMDS_MeshNode *n12 [4], *n13 [4];
2499         bool Ok12 = false, Ok13 = false;
2500         const SMDS_MeshNode *linkNode1, *linkNode2;
2501         if(tr2) {
2502           linkNode1 = link12->first;
2503           linkNode2 = link12->second;
2504           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2505             Ok12 = true;
2506         }
2507         if(tr3) {
2508           linkNode1 = link13->first;
2509           linkNode2 = link13->second;
2510           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2511             Ok13 = true;
2512         }
2513
2514         // Choose a pair to fuse
2515         if ( Ok12 && Ok13 ) {
2516           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2517           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2518           double aBadRate12 = getBadRate( &quad12, theCrit );
2519           double aBadRate13 = getBadRate( &quad13, theCrit );
2520           if (  aBadRate13 < aBadRate12 )
2521             Ok12 = false;
2522           else
2523             Ok13 = false;
2524         }
2525
2526         // Make quadrangles
2527         // and remove fused elems and removed links from the maps
2528         mapEl_setLi.erase( tr1 );
2529         if ( Ok12 ) {
2530           mapEl_setLi.erase( tr2 );
2531           mapLi_listEl.erase( *link12 );
2532           if(tr1->NbNodes()==3) {
2533             const SMDS_MeshElement* newElem = 0;
2534             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2535             myLastCreatedElems.Append(newElem);
2536             AddToSameGroups( newElem, tr1, aMesh );
2537             int aShapeId = tr1->getshapeId();
2538             if ( aShapeId )
2539               {
2540                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2541               }
2542             aMesh->RemoveElement( tr1 );
2543             aMesh->RemoveElement( tr2 );
2544           }
2545           else {
2546             const SMDS_MeshNode* N1 [6];
2547             const SMDS_MeshNode* N2 [6];
2548             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2549             // now we receive following N1 and N2 (using numeration as above image)
2550             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2551             // i.e. first nodes from both arrays determ new diagonal
2552             const SMDS_MeshNode* aNodes[8];
2553             aNodes[0] = N1[0];
2554             aNodes[1] = N1[1];
2555             aNodes[2] = N2[0];
2556             aNodes[3] = N2[1];
2557             aNodes[4] = N1[3];
2558             aNodes[5] = N2[5];
2559             aNodes[6] = N2[3];
2560             aNodes[7] = N1[5];
2561             const SMDS_MeshElement* newElem = 0;
2562             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2563                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2564             myLastCreatedElems.Append(newElem);
2565             AddToSameGroups( newElem, tr1, aMesh );
2566             int aShapeId = tr1->getshapeId();
2567             if ( aShapeId )
2568               {
2569                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2570               }
2571             aMesh->RemoveElement( tr1 );
2572             aMesh->RemoveElement( tr2 );
2573             // remove middle node (9)
2574             GetMeshDS()->RemoveNode( N1[4] );
2575           }
2576         }
2577         else if ( Ok13 ) {
2578           mapEl_setLi.erase( tr3 );
2579           mapLi_listEl.erase( *link13 );
2580           if(tr1->NbNodes()==3) {
2581             const SMDS_MeshElement* newElem = 0;
2582             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2583             myLastCreatedElems.Append(newElem);
2584             AddToSameGroups( newElem, tr1, aMesh );
2585             int aShapeId = tr1->getshapeId();
2586             if ( aShapeId )
2587               {
2588                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2589               }
2590             aMesh->RemoveElement( tr1 );
2591             aMesh->RemoveElement( tr3 );
2592           }
2593           else {
2594             const SMDS_MeshNode* N1 [6];
2595             const SMDS_MeshNode* N2 [6];
2596             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2597             // now we receive following N1 and N2 (using numeration as above image)
2598             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2599             // i.e. first nodes from both arrays determ new diagonal
2600             const SMDS_MeshNode* aNodes[8];
2601             aNodes[0] = N1[0];
2602             aNodes[1] = N1[1];
2603             aNodes[2] = N2[0];
2604             aNodes[3] = N2[1];
2605             aNodes[4] = N1[3];
2606             aNodes[5] = N2[5];
2607             aNodes[6] = N2[3];
2608             aNodes[7] = N1[5];
2609             const SMDS_MeshElement* newElem = 0;
2610             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2611                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2612             myLastCreatedElems.Append(newElem);
2613             AddToSameGroups( newElem, tr1, aMesh );
2614             int aShapeId = tr1->getshapeId();
2615             if ( aShapeId )
2616               {
2617                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2618               }
2619             aMesh->RemoveElement( tr1 );
2620             aMesh->RemoveElement( tr3 );
2621             // remove middle node (9)
2622             GetMeshDS()->RemoveNode( N1[4] );
2623           }
2624         }
2625
2626         // Next element to fuse: the rejected one
2627         if ( tr3 )
2628           startElem = Ok12 ? tr3 : tr2;
2629
2630       } // if ( startElem )
2631     } // while ( startElem || !startLinks.empty() )
2632   } // while ( ! mapEl_setLi.empty() )
2633
2634   return true;
2635 }
2636
2637
2638 /*#define DUMPSO(txt) \
2639 //  cout << txt << endl;
2640 //=============================================================================
2641 //
2642 //
2643 //
2644 //=============================================================================
2645 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2646 {
2647 if ( i1 == i2 )
2648 return;
2649 int tmp = idNodes[ i1 ];
2650 idNodes[ i1 ] = idNodes[ i2 ];
2651 idNodes[ i2 ] = tmp;
2652 gp_Pnt Ptmp = P[ i1 ];
2653 P[ i1 ] = P[ i2 ];
2654 P[ i2 ] = Ptmp;
2655 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2656 }
2657
2658 //=======================================================================
2659 //function : SortQuadNodes
2660 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2661 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2662 //           1 or 2 else 0.
2663 //=======================================================================
2664
2665 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2666 int               idNodes[] )
2667 {
2668   gp_Pnt P[4];
2669   int i;
2670   for ( i = 0; i < 4; i++ ) {
2671     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2672     if ( !n ) return 0;
2673     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2674   }
2675
2676   gp_Vec V1(P[0], P[1]);
2677   gp_Vec V2(P[0], P[2]);
2678   gp_Vec V3(P[0], P[3]);
2679
2680   gp_Vec Cross1 = V1 ^ V2;
2681   gp_Vec Cross2 = V2 ^ V3;
2682
2683   i = 0;
2684   if (Cross1.Dot(Cross2) < 0)
2685   {
2686     Cross1 = V2 ^ V1;
2687     Cross2 = V1 ^ V3;
2688
2689     if (Cross1.Dot(Cross2) < 0)
2690       i = 2;
2691     else
2692       i = 1;
2693     swap ( i, i + 1, idNodes, P );
2694
2695     //     for ( int ii = 0; ii < 4; ii++ ) {
2696     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2697     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2698     //     }
2699   }
2700   return i;
2701 }
2702
2703 //=======================================================================
2704 //function : SortHexaNodes
2705 //purpose  : Set 8 nodes of a hexahedron in a good order.
2706 //           Return success status
2707 //=======================================================================
2708
2709 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2710                                       int               idNodes[] )
2711 {
2712   gp_Pnt P[8];
2713   int i;
2714   DUMPSO( "INPUT: ========================================");
2715   for ( i = 0; i < 8; i++ ) {
2716     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2717     if ( !n ) return false;
2718     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2719     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2720   }
2721   DUMPSO( "========================================");
2722
2723
2724   set<int> faceNodes;  // ids of bottom face nodes, to be found
2725   set<int> checkedId1; // ids of tried 2-nd nodes
2726   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2727   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2728   int iMin, iLoop1 = 0;
2729
2730   // Loop to try the 2-nd nodes
2731
2732   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2733   {
2734     // Find not checked 2-nd node
2735     for ( i = 1; i < 8; i++ )
2736       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2737         int id1 = idNodes[i];
2738         swap ( 1, i, idNodes, P );
2739         checkedId1.insert ( id1 );
2740         break;
2741       }
2742
2743     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2744     // ie that all but meybe one (id3 which is on the same face) nodes
2745     // lay on the same side from the triangle plane.
2746
2747     bool manyInPlane = false; // more than 4 nodes lay in plane
2748     int iLoop2 = 0;
2749     while ( ++iLoop2 < 6 ) {
2750
2751       // get 1-2-3 plane coeffs
2752       Standard_Real A, B, C, D;
2753       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2754       if ( N.SquareMagnitude() > gp::Resolution() )
2755       {
2756         gp_Pln pln ( P[0], N );
2757         pln.Coefficients( A, B, C, D );
2758
2759         // find the node (iMin) closest to pln
2760         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2761         set<int> idInPln;
2762         for ( i = 3; i < 8; i++ ) {
2763           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2764           if ( fabs( dist[i] ) < minDist ) {
2765             minDist = fabs( dist[i] );
2766             iMin = i;
2767           }
2768           if ( fabs( dist[i] ) <= tol )
2769             idInPln.insert( idNodes[i] );
2770         }
2771
2772         // there should not be more than 4 nodes in bottom plane
2773         if ( idInPln.size() > 1 )
2774         {
2775           DUMPSO( "### idInPln.size() = " << idInPln.size());
2776           // idInPlane does not contain the first 3 nodes
2777           if ( manyInPlane || idInPln.size() == 5)
2778             return false; // all nodes in one plane
2779           manyInPlane = true;
2780
2781           // set the 1-st node to be not in plane
2782           for ( i = 3; i < 8; i++ ) {
2783             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2784               DUMPSO( "### Reset 0-th node");
2785               swap( 0, i, idNodes, P );
2786               break;
2787             }
2788           }
2789
2790           // reset to re-check second nodes
2791           leastDist = DBL_MAX;
2792           faceNodes.clear();
2793           checkedId1.clear();
2794           iLoop1 = 0;
2795           break; // from iLoop2;
2796         }
2797
2798         // check that the other 4 nodes are on the same side
2799         bool sameSide = true;
2800         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2801         for ( i = 3; sameSide && i < 8; i++ ) {
2802           if ( i != iMin )
2803             sameSide = ( isNeg == dist[i] <= 0.);
2804         }
2805
2806         // keep best solution
2807         if ( sameSide && minDist < leastDist ) {
2808           leastDist = minDist;
2809           faceNodes.clear();
2810           faceNodes.insert( idNodes[ 1 ] );
2811           faceNodes.insert( idNodes[ 2 ] );
2812           faceNodes.insert( idNodes[ iMin ] );
2813           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2814                   << " leastDist = " << leastDist);
2815           if ( leastDist <= DBL_MIN )
2816             break;
2817         }
2818       }
2819
2820       // set next 3-d node to check
2821       int iNext = 2 + iLoop2;
2822       if ( iNext < 8 ) {
2823         DUMPSO( "Try 2-nd");
2824         swap ( 2, iNext, idNodes, P );
2825       }
2826     } // while ( iLoop2 < 6 )
2827   } // iLoop1
2828
2829   if ( faceNodes.empty() ) return false;
2830
2831   // Put the faceNodes in proper places
2832   for ( i = 4; i < 8; i++ ) {
2833     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2834       // find a place to put
2835       int iTo = 1;
2836       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2837         iTo++;
2838       DUMPSO( "Set faceNodes");
2839       swap ( iTo, i, idNodes, P );
2840     }
2841   }
2842
2843
2844   // Set nodes of the found bottom face in good order
2845   DUMPSO( " Found bottom face: ");
2846   i = SortQuadNodes( theMesh, idNodes );
2847   if ( i ) {
2848     gp_Pnt Ptmp = P[ i ];
2849     P[ i ] = P[ i+1 ];
2850     P[ i+1 ] = Ptmp;
2851   }
2852   //   else
2853   //     for ( int ii = 0; ii < 4; ii++ ) {
2854   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2855   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2856   //    }
2857
2858   // Gravity center of the top and bottom faces
2859   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2860   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2861
2862   // Get direction from the bottom to the top face
2863   gp_Vec upDir ( aGCb, aGCt );
2864   Standard_Real upDirSize = upDir.Magnitude();
2865   if ( upDirSize <= gp::Resolution() ) return false;
2866   upDir / upDirSize;
2867
2868   // Assure that the bottom face normal points up
2869   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2870   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2871   if ( Nb.Dot( upDir ) < 0 ) {
2872     DUMPSO( "Reverse bottom face");
2873     swap( 1, 3, idNodes, P );
2874   }
2875
2876   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2877   Standard_Real minDist = DBL_MAX;
2878   for ( i = 4; i < 8; i++ ) {
2879     // projection of P[i] to the plane defined by P[0] and upDir
2880     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2881     Standard_Real sqDist = P[0].SquareDistance( Pp );
2882     if ( sqDist < minDist ) {
2883       minDist = sqDist;
2884       iMin = i;
2885     }
2886   }
2887   DUMPSO( "Set 4-th");
2888   swap ( 4, iMin, idNodes, P );
2889
2890   // Set nodes of the top face in good order
2891   DUMPSO( "Sort top face");
2892   i = SortQuadNodes( theMesh, &idNodes[4] );
2893   if ( i ) {
2894     i += 4;
2895     gp_Pnt Ptmp = P[ i ];
2896     P[ i ] = P[ i+1 ];
2897     P[ i+1 ] = Ptmp;
2898   }
2899
2900   // Assure that direction of the top face normal is from the bottom face
2901   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2902   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2903   if ( Nt.Dot( upDir ) < 0 ) {
2904     DUMPSO( "Reverse top face");
2905     swap( 5, 7, idNodes, P );
2906   }
2907
2908   //   DUMPSO( "OUTPUT: ========================================");
2909   //   for ( i = 0; i < 8; i++ ) {
2910   //     float *p = ugrid->GetPoint(idNodes[i]);
2911   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2912   //   }
2913
2914   return true;
2915 }*/
2916
2917 //================================================================================
2918 /*!
2919  * \brief Return nodes linked to the given one
2920  * \param theNode - the node
2921  * \param linkedNodes - the found nodes
2922  * \param type - the type of elements to check
2923  *
2924  * Medium nodes are ignored
2925  */
2926 //================================================================================
2927
2928 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2929                                        TIDSortedElemSet &   linkedNodes,
2930                                        SMDSAbs_ElementType  type )
2931 {
2932   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2933   while ( elemIt->more() )
2934   {
2935     const SMDS_MeshElement* elem = elemIt->next();
2936     if(elem->GetType() == SMDSAbs_0DElement)
2937       continue;
2938
2939     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2940     if ( elem->GetType() == SMDSAbs_Volume )
2941     {
2942       SMDS_VolumeTool vol( elem );
2943       while ( nodeIt->more() ) {
2944         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2945         if ( theNode != n && vol.IsLinked( theNode, n ))
2946           linkedNodes.insert( n );
2947       }
2948     }
2949     else
2950     {
2951       for ( int i = 0; nodeIt->more(); ++i ) {
2952         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2953         if ( n == theNode ) {
2954           int iBefore = i - 1;
2955           int iAfter  = i + 1;
2956           if ( elem->IsQuadratic() ) {
2957             int nb = elem->NbNodes() / 2;
2958             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2959             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2960           }
2961           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2962           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2963         }
2964       }
2965     }
2966   }
2967 }
2968
2969 //=======================================================================
2970 //function : laplacianSmooth
2971 //purpose  : pulls theNode toward the center of surrounding nodes directly
2972 //           connected to that node along an element edge
2973 //=======================================================================
2974
2975 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2976                      const Handle(Geom_Surface)&          theSurface,
2977                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2978 {
2979   // find surrounding nodes
2980
2981   TIDSortedElemSet nodeSet;
2982   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2983
2984   // compute new coodrs
2985
2986   double coord[] = { 0., 0., 0. };
2987   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2988   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2989     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2990     if ( theSurface.IsNull() ) { // smooth in 3D
2991       coord[0] += node->X();
2992       coord[1] += node->Y();
2993       coord[2] += node->Z();
2994     }
2995     else { // smooth in 2D
2996       ASSERT( theUVMap.find( node ) != theUVMap.end() );
2997       gp_XY* uv = theUVMap[ node ];
2998       coord[0] += uv->X();
2999       coord[1] += uv->Y();
3000     }
3001   }
3002   int nbNodes = nodeSet.size();
3003   if ( !nbNodes )
3004     return;
3005   coord[0] /= nbNodes;
3006   coord[1] /= nbNodes;
3007
3008   if ( !theSurface.IsNull() ) {
3009     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3010     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3011     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3012     coord[0] = p3d.X();
3013     coord[1] = p3d.Y();
3014     coord[2] = p3d.Z();
3015   }
3016   else
3017     coord[2] /= nbNodes;
3018
3019   // move node
3020
3021   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3022 }
3023
3024 //=======================================================================
3025 //function : centroidalSmooth
3026 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3027 //           surrounding elements
3028 //=======================================================================
3029
3030 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3031                       const Handle(Geom_Surface)&          theSurface,
3032                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3033 {
3034   gp_XYZ aNewXYZ(0.,0.,0.);
3035   SMESH::Controls::Area anAreaFunc;
3036   double totalArea = 0.;
3037   int nbElems = 0;
3038
3039   // compute new XYZ
3040
3041   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3042   while ( elemIt->more() )
3043   {
3044     const SMDS_MeshElement* elem = elemIt->next();
3045     nbElems++;
3046
3047     gp_XYZ elemCenter(0.,0.,0.);
3048     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3049     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3050     int nn = elem->NbNodes();
3051     if(elem->IsQuadratic()) nn = nn/2;
3052     int i=0;
3053     //while ( itN->more() ) {
3054     while ( i<nn ) {
3055       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3056       i++;
3057       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3058       aNodePoints.push_back( aP );
3059       if ( !theSurface.IsNull() ) { // smooth in 2D
3060         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3061         gp_XY* uv = theUVMap[ aNode ];
3062         aP.SetCoord( uv->X(), uv->Y(), 0. );
3063       }
3064       elemCenter += aP;
3065     }
3066     double elemArea = anAreaFunc.GetValue( aNodePoints );
3067     totalArea += elemArea;
3068     elemCenter /= nn;
3069     aNewXYZ += elemCenter * elemArea;
3070   }
3071   aNewXYZ /= totalArea;
3072   if ( !theSurface.IsNull() ) {
3073     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3074     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3075   }
3076
3077   // move node
3078
3079   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3080 }
3081
3082 //=======================================================================
3083 //function : getClosestUV
3084 //purpose  : return UV of closest projection
3085 //=======================================================================
3086
3087 static bool getClosestUV (Extrema_GenExtPS& projector,
3088                           const gp_Pnt&     point,
3089                           gp_XY &           result)
3090 {
3091   projector.Perform( point );
3092   if ( projector.IsDone() ) {
3093     double u, v, minVal = DBL_MAX;
3094     for ( int i = projector.NbExt(); i > 0; i-- )
3095 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3096       if ( projector.SquareDistance( i ) < minVal ) {
3097         minVal = projector.SquareDistance( i );
3098 #else
3099       if ( projector.Value( i ) < minVal ) {
3100         minVal = projector.Value( i );
3101 #endif
3102         projector.Point( i ).Parameter( u, v );
3103       }
3104     result.SetCoord( u, v );
3105     return true;
3106   }
3107   return false;
3108 }
3109
3110 //=======================================================================
3111 //function : Smooth
3112 //purpose  : Smooth theElements during theNbIterations or until a worst
3113 //           element has aspect ratio <= theTgtAspectRatio.
3114 //           Aspect Ratio varies in range [1.0, inf].
3115 //           If theElements is empty, the whole mesh is smoothed.
3116 //           theFixedNodes contains additionally fixed nodes. Nodes built
3117 //           on edges and boundary nodes are always fixed.
3118 //=======================================================================
3119
3120 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3121                                set<const SMDS_MeshNode*> & theFixedNodes,
3122                                const SmoothMethod          theSmoothMethod,
3123                                const int                   theNbIterations,
3124                                double                      theTgtAspectRatio,
3125                                const bool                  the2D)
3126 {
3127   myLastCreatedElems.Clear();
3128   myLastCreatedNodes.Clear();
3129
3130   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3131
3132   if ( theTgtAspectRatio < 1.0 )
3133     theTgtAspectRatio = 1.0;
3134
3135   const double disttol = 1.e-16;
3136
3137   SMESH::Controls::AspectRatio aQualityFunc;
3138
3139   SMESHDS_Mesh* aMesh = GetMeshDS();
3140
3141   if ( theElems.empty() ) {
3142     // add all faces to theElems
3143     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3144     while ( fIt->more() ) {
3145       const SMDS_MeshElement* face = fIt->next();
3146       theElems.insert( theElems.end(), face );
3147     }
3148   }
3149   // get all face ids theElems are on
3150   set< int > faceIdSet;
3151   TIDSortedElemSet::iterator itElem;
3152   if ( the2D )
3153     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3154       int fId = FindShape( *itElem );
3155       // check that corresponding submesh exists and a shape is face
3156       if (fId &&
3157           faceIdSet.find( fId ) == faceIdSet.end() &&
3158           aMesh->MeshElements( fId )) {
3159         TopoDS_Shape F = aMesh->IndexToShape( fId );
3160         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3161           faceIdSet.insert( fId );
3162       }
3163     }
3164   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3165
3166   // ===============================================
3167   // smooth elements on each TopoDS_Face separately
3168   // ===============================================
3169
3170   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3171   for ( ; fId != faceIdSet.rend(); ++fId ) {
3172     // get face surface and submesh
3173     Handle(Geom_Surface) surface;
3174     SMESHDS_SubMesh* faceSubMesh = 0;
3175     TopoDS_Face face;
3176     double fToler2 = 0, f,l;
3177     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3178     bool isUPeriodic = false, isVPeriodic = false;
3179     if ( *fId ) {
3180       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3181       surface = BRep_Tool::Surface( face );
3182       faceSubMesh = aMesh->MeshElements( *fId );
3183       fToler2 = BRep_Tool::Tolerance( face );
3184       fToler2 *= fToler2 * 10.;
3185       isUPeriodic = surface->IsUPeriodic();
3186       if ( isUPeriodic )
3187         surface->UPeriod();
3188       isVPeriodic = surface->IsVPeriodic();
3189       if ( isVPeriodic )
3190         surface->VPeriod();
3191       surface->Bounds( u1, u2, v1, v2 );
3192     }
3193     // ---------------------------------------------------------
3194     // for elements on a face, find movable and fixed nodes and
3195     // compute UV for them
3196     // ---------------------------------------------------------
3197     bool checkBoundaryNodes = false;
3198     bool isQuadratic = false;
3199     set<const SMDS_MeshNode*> setMovableNodes;
3200     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3201     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3202     list< const SMDS_MeshElement* > elemsOnFace;
3203
3204     Extrema_GenExtPS projector;
3205     GeomAdaptor_Surface surfAdaptor;
3206     if ( !surface.IsNull() ) {
3207       surfAdaptor.Load( surface );
3208       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3209     }
3210     int nbElemOnFace = 0;
3211     itElem = theElems.begin();
3212     // loop on not yet smoothed elements: look for elems on a face
3213     while ( itElem != theElems.end() ) {
3214       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3215         break; // all elements found
3216
3217       const SMDS_MeshElement* elem = *itElem;
3218       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3219            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3220         ++itElem;
3221         continue;
3222       }
3223       elemsOnFace.push_back( elem );
3224       theElems.erase( itElem++ );
3225       nbElemOnFace++;
3226
3227       if ( !isQuadratic )
3228         isQuadratic = elem->IsQuadratic();
3229
3230       // get movable nodes of elem
3231       const SMDS_MeshNode* node;
3232       SMDS_TypeOfPosition posType;
3233       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3234       int nn = 0, nbn =  elem->NbNodes();
3235       if(elem->IsQuadratic())
3236         nbn = nbn/2;
3237       while ( nn++ < nbn ) {
3238         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3239         const SMDS_PositionPtr& pos = node->GetPosition();
3240         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3241         if (posType != SMDS_TOP_EDGE &&
3242             posType != SMDS_TOP_VERTEX &&
3243             theFixedNodes.find( node ) == theFixedNodes.end())
3244         {
3245           // check if all faces around the node are on faceSubMesh
3246           // because a node on edge may be bound to face
3247           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3248           bool all = true;
3249           if ( faceSubMesh ) {
3250             while ( eIt->more() && all ) {
3251               const SMDS_MeshElement* e = eIt->next();
3252               all = faceSubMesh->Contains( e );
3253             }
3254           }
3255           if ( all )
3256             setMovableNodes.insert( node );
3257           else
3258             checkBoundaryNodes = true;
3259         }
3260         if ( posType == SMDS_TOP_3DSPACE )
3261           checkBoundaryNodes = true;
3262       }
3263
3264       if ( surface.IsNull() )
3265         continue;
3266
3267       // get nodes to check UV
3268       list< const SMDS_MeshNode* > uvCheckNodes;
3269       itN = elem->nodesIterator();
3270       nn = 0; nbn =  elem->NbNodes();
3271       if(elem->IsQuadratic())
3272         nbn = nbn/2;
3273       while ( nn++ < nbn ) {
3274         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3275         if ( uvMap.find( node ) == uvMap.end() )
3276           uvCheckNodes.push_back( node );
3277         // add nodes of elems sharing node
3278         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3279         //         while ( eIt->more() ) {
3280         //           const SMDS_MeshElement* e = eIt->next();
3281         //           if ( e != elem ) {
3282         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3283         //             while ( nIt->more() ) {
3284         //               const SMDS_MeshNode* n =
3285         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3286         //               if ( uvMap.find( n ) == uvMap.end() )
3287         //                 uvCheckNodes.push_back( n );
3288         //             }
3289         //           }
3290         //         }
3291       }
3292       // check UV on face
3293       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3294       for ( ; n != uvCheckNodes.end(); ++n ) {
3295         node = *n;
3296         gp_XY uv( 0, 0 );
3297         const SMDS_PositionPtr& pos = node->GetPosition();
3298         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3299         // get existing UV
3300         switch ( posType ) {
3301         case SMDS_TOP_FACE: {
3302           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3303           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3304           break;
3305         }
3306         case SMDS_TOP_EDGE: {
3307           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3308           Handle(Geom2d_Curve) pcurve;
3309           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3310             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3311           if ( !pcurve.IsNull() ) {
3312             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3313             uv = pcurve->Value( u ).XY();
3314           }
3315           break;
3316         }
3317         case SMDS_TOP_VERTEX: {
3318           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3319           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3320             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3321           break;
3322         }
3323         default:;
3324         }
3325         // check existing UV
3326         bool project = true;
3327         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3328         double dist1 = DBL_MAX, dist2 = 0;
3329         if ( posType != SMDS_TOP_3DSPACE ) {
3330           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3331           project = dist1 > fToler2;
3332         }
3333         if ( project ) { // compute new UV
3334           gp_XY newUV;
3335           if ( !getClosestUV( projector, pNode, newUV )) {
3336             MESSAGE("Node Projection Failed " << node);
3337           }
3338           else {
3339             if ( isUPeriodic )
3340               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3341             if ( isVPeriodic )
3342               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3343             // check new UV
3344             if ( posType != SMDS_TOP_3DSPACE )
3345               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3346             if ( dist2 < dist1 )
3347               uv = newUV;
3348           }
3349         }
3350         // store UV in the map
3351         listUV.push_back( uv );
3352         uvMap.insert( make_pair( node, &listUV.back() ));
3353       }
3354     } // loop on not yet smoothed elements
3355
3356     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3357       checkBoundaryNodes = true;
3358
3359     // fix nodes on mesh boundary
3360
3361     if ( checkBoundaryNodes ) {
3362       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3363       map< SMESH_TLink, int >::iterator link_nb;
3364       // put all elements links to linkNbMap
3365       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3366       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3367         const SMDS_MeshElement* elem = (*elemIt);
3368         int nbn =  elem->NbCornerNodes();
3369         // loop on elem links: insert them in linkNbMap
3370         for ( int iN = 0; iN < nbn; ++iN ) {
3371           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3372           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3373           SMESH_TLink link( n1, n2 );
3374           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3375           link_nb->second++;
3376         }
3377       }
3378       // remove nodes that are in links encountered only once from setMovableNodes
3379       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3380         if ( link_nb->second == 1 ) {
3381           setMovableNodes.erase( link_nb->first.node1() );
3382           setMovableNodes.erase( link_nb->first.node2() );
3383         }
3384       }
3385     }
3386
3387     // -----------------------------------------------------
3388     // for nodes on seam edge, compute one more UV ( uvMap2 );
3389     // find movable nodes linked to nodes on seam and which
3390     // are to be smoothed using the second UV ( uvMap2 )
3391     // -----------------------------------------------------
3392
3393     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3394     if ( !surface.IsNull() ) {
3395       TopExp_Explorer eExp( face, TopAbs_EDGE );
3396       for ( ; eExp.More(); eExp.Next() ) {
3397         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3398         if ( !BRep_Tool::IsClosed( edge, face ))
3399           continue;
3400         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3401         if ( !sm ) continue;
3402         // find out which parameter varies for a node on seam
3403         double f,l;
3404         gp_Pnt2d uv1, uv2;
3405         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3406         if ( pcurve.IsNull() ) continue;
3407         uv1 = pcurve->Value( f );
3408         edge.Reverse();
3409         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3410         if ( pcurve.IsNull() ) continue;
3411         uv2 = pcurve->Value( f );
3412         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3413         // assure uv1 < uv2
3414         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3415           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3416         }
3417         // get nodes on seam and its vertices
3418         list< const SMDS_MeshNode* > seamNodes;
3419         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3420         while ( nSeamIt->more() ) {
3421           const SMDS_MeshNode* node = nSeamIt->next();
3422           if ( !isQuadratic || !IsMedium( node ))
3423             seamNodes.push_back( node );
3424         }
3425         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3426         for ( ; vExp.More(); vExp.Next() ) {
3427           sm = aMesh->MeshElements( vExp.Current() );
3428           if ( sm ) {
3429             nSeamIt = sm->GetNodes();
3430             while ( nSeamIt->more() )
3431               seamNodes.push_back( nSeamIt->next() );
3432           }
3433         }
3434         // loop on nodes on seam
3435         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3436         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3437           const SMDS_MeshNode* nSeam = *noSeIt;
3438           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3439           if ( n_uv == uvMap.end() )
3440             continue;
3441           // set the first UV
3442           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3443           // set the second UV
3444           listUV.push_back( *n_uv->second );
3445           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3446           if ( uvMap2.empty() )
3447             uvMap2 = uvMap; // copy the uvMap contents
3448           uvMap2[ nSeam ] = &listUV.back();
3449
3450           // collect movable nodes linked to ones on seam in nodesNearSeam
3451           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3452           while ( eIt->more() ) {
3453             const SMDS_MeshElement* e = eIt->next();
3454             int nbUseMap1 = 0, nbUseMap2 = 0;
3455             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3456             int nn = 0, nbn =  e->NbNodes();
3457             if(e->IsQuadratic()) nbn = nbn/2;
3458             while ( nn++ < nbn )
3459             {
3460               const SMDS_MeshNode* n =
3461                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3462               if (n == nSeam ||
3463                   setMovableNodes.find( n ) == setMovableNodes.end() )
3464                 continue;
3465               // add only nodes being closer to uv2 than to uv1
3466               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3467                            0.5 * ( n->Y() + nSeam->Y() ),
3468                            0.5 * ( n->Z() + nSeam->Z() ));
3469               gp_XY uv;
3470               getClosestUV( projector, pMid, uv );
3471               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3472                 nodesNearSeam.insert( n );
3473                 nbUseMap2++;
3474               }
3475               else
3476                 nbUseMap1++;
3477             }
3478             // for centroidalSmooth all element nodes must
3479             // be on one side of a seam
3480             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3481               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3482               nn = 0;
3483               while ( nn++ < nbn ) {
3484                 const SMDS_MeshNode* n =
3485                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3486                 setMovableNodes.erase( n );
3487               }
3488             }
3489           }
3490         } // loop on nodes on seam
3491       } // loop on edge of a face
3492     } // if ( !face.IsNull() )
3493
3494     if ( setMovableNodes.empty() ) {
3495       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3496       continue; // goto next face
3497     }
3498
3499     // -------------
3500     // SMOOTHING //
3501     // -------------
3502
3503     int it = -1;
3504     double maxRatio = -1., maxDisplacement = -1.;
3505     set<const SMDS_MeshNode*>::iterator nodeToMove;
3506     for ( it = 0; it < theNbIterations; it++ ) {
3507       maxDisplacement = 0.;
3508       nodeToMove = setMovableNodes.begin();
3509       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3510         const SMDS_MeshNode* node = (*nodeToMove);
3511         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3512
3513         // smooth
3514         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3515         if ( theSmoothMethod == LAPLACIAN )
3516           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3517         else
3518           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3519
3520         // node displacement
3521         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3522         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3523         if ( aDispl > maxDisplacement )
3524           maxDisplacement = aDispl;
3525       }
3526       // no node movement => exit
3527       //if ( maxDisplacement < 1.e-16 ) {
3528       if ( maxDisplacement < disttol ) {
3529         MESSAGE("-- no node movement --");
3530         break;
3531       }
3532
3533       // check elements quality
3534       maxRatio  = 0;
3535       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3536       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3537         const SMDS_MeshElement* elem = (*elemIt);
3538         if ( !elem || elem->GetType() != SMDSAbs_Face )
3539           continue;
3540         SMESH::Controls::TSequenceOfXYZ aPoints;
3541         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3542           double aValue = aQualityFunc.GetValue( aPoints );
3543           if ( aValue > maxRatio )
3544             maxRatio = aValue;
3545         }
3546       }
3547       if ( maxRatio <= theTgtAspectRatio ) {
3548         MESSAGE("-- quality achived --");
3549         break;
3550       }
3551       if (it+1 == theNbIterations) {
3552         MESSAGE("-- Iteration limit exceeded --");
3553       }
3554     } // smoothing iterations
3555
3556     MESSAGE(" Face id: " << *fId <<
3557             " Nb iterstions: " << it <<
3558             " Displacement: " << maxDisplacement <<
3559             " Aspect Ratio " << maxRatio);
3560
3561     // ---------------------------------------
3562     // new nodes positions are computed,
3563     // record movement in DS and set new UV
3564     // ---------------------------------------
3565     nodeToMove = setMovableNodes.begin();
3566     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3567       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3568       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3569       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3570       if ( node_uv != uvMap.end() ) {
3571         gp_XY* uv = node_uv->second;
3572         node->SetPosition
3573           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3574       }
3575     }
3576
3577     // move medium nodes of quadratic elements
3578     if ( isQuadratic )
3579     {
3580       SMESH_MesherHelper helper( *GetMesh() );
3581       if ( !face.IsNull() )
3582         helper.SetSubShape( face );
3583       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3584       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3585         const SMDS_VtkFace* QF =
3586           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3587         if(QF && QF->IsQuadratic()) {
3588           vector<const SMDS_MeshNode*> Ns;
3589           Ns.reserve(QF->NbNodes()+1);
3590           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3591           while ( anIter->more() )
3592             Ns.push_back( cast2Node(anIter->next()) );
3593           Ns.push_back( Ns[0] );
3594           double x, y, z;
3595           for(int i=0; i<QF->NbNodes(); i=i+2) {
3596             if ( !surface.IsNull() ) {
3597               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3598               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3599               gp_XY uv = ( uv1 + uv2 ) / 2.;
3600               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3601               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3602             }
3603             else {
3604               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3605               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3606               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3607             }
3608             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3609                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3610                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3611               // we have to move i+1 node
3612               aMesh->MoveNode( Ns[i+1], x, y, z );
3613             }
3614           }
3615         }
3616       }
3617     }
3618
3619   } // loop on face ids
3620
3621 }
3622
3623 //=======================================================================
3624 //function : isReverse
3625 //purpose  : Return true if normal of prevNodes is not co-directied with
3626 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3627 //           iNotSame is where prevNodes and nextNodes are different.
3628 //           If result is true then future volume orientation is OK
3629 //=======================================================================
3630
3631 static bool isReverse(const SMDS_MeshElement*             face,
3632                       const vector<const SMDS_MeshNode*>& prevNodes,
3633                       const vector<const SMDS_MeshNode*>& nextNodes,
3634                       const int                           iNotSame)
3635 {
3636
3637   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3638   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3639   gp_XYZ extrDir( pN - pP ), faceNorm;
3640   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3641
3642   return faceNorm * extrDir < 0.0;
3643 }
3644
3645 //=======================================================================
3646 /*!
3647  * \brief Create elements by sweeping an element
3648  * \param elem - element to sweep
3649  * \param newNodesItVec - nodes generated from each node of the element
3650  * \param newElems - generated elements
3651  * \param nbSteps - number of sweeping steps
3652  * \param srcElements - to append elem for each generated element
3653  */
3654 //=======================================================================
3655
3656 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3657                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3658                                     list<const SMDS_MeshElement*>&        newElems,
3659                                     const int                             nbSteps,
3660                                     SMESH_SequenceOfElemPtr&              srcElements)
3661 {
3662   //MESSAGE("sweepElement " << nbSteps);
3663   SMESHDS_Mesh* aMesh = GetMeshDS();
3664
3665   const int           nbNodes = elem->NbNodes();
3666   const int         nbCorners = elem->NbCornerNodes();
3667   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3668                                                           polyhedron creation !!! */
3669   // Loop on elem nodes:
3670   // find new nodes and detect same nodes indices
3671   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3672   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3673   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3674   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3675
3676   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3677   vector<int> sames(nbNodes);
3678   vector<bool> isSingleNode(nbNodes);
3679
3680   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3681     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3682     const SMDS_MeshNode*                         node = nnIt->first;
3683     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3684     if ( listNewNodes.empty() )
3685       return;
3686
3687     itNN   [ iNode ] = listNewNodes.begin();
3688     prevNod[ iNode ] = node;
3689     nextNod[ iNode ] = listNewNodes.front();
3690
3691     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3692                                                              corner node of linear */
3693     if ( prevNod[ iNode ] != nextNod [ iNode ])
3694       nbDouble += !isSingleNode[iNode];
3695
3696     if( iNode < nbCorners ) { // check corners only
3697       if ( prevNod[ iNode ] == nextNod [ iNode ])
3698         sames[nbSame++] = iNode;
3699       else
3700         iNotSameNode = iNode;
3701     }
3702   }
3703
3704   if ( nbSame == nbNodes || nbSame > 2) {
3705     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3706     return;
3707   }
3708
3709   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3710   {
3711     // fix nodes order to have bottom normal external
3712     if ( baseType == SMDSEntity_Polygon )
3713     {
3714       std::reverse( itNN.begin(), itNN.end() );
3715       std::reverse( prevNod.begin(), prevNod.end() );
3716       std::reverse( midlNod.begin(), midlNod.end() );
3717       std::reverse( nextNod.begin(), nextNod.end() );
3718       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3719     }
3720     else
3721     {
3722       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3723       SMDS_MeshCell::applyInterlace( ind, itNN );
3724       SMDS_MeshCell::applyInterlace( ind, prevNod );
3725       SMDS_MeshCell::applyInterlace( ind, nextNod );
3726       SMDS_MeshCell::applyInterlace( ind, midlNod );
3727       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3728       if ( nbSame > 0 )
3729       {
3730         sames[nbSame] = iNotSameNode;
3731         for ( int j = 0; j <= nbSame; ++j )
3732           for ( size_t i = 0; i < ind.size(); ++i )
3733             if ( ind[i] == sames[j] )
3734             {
3735               sames[j] = i;
3736               break;
3737             }
3738         iNotSameNode = sames[nbSame];
3739       }
3740     }
3741   }
3742
3743   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3744   if ( nbSame > 0 ) {
3745     iSameNode    = sames[ nbSame-1 ];
3746     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3747     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3748     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3749   }
3750
3751   // make new elements
3752   for (int iStep = 0; iStep < nbSteps; iStep++ )
3753   {
3754     // get next nodes
3755     for ( iNode = 0; iNode < nbNodes; iNode++ )
3756     {
3757       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3758       nextNod[ iNode ] = *itNN[ iNode ]++;
3759     }
3760
3761     SMDS_MeshElement* aNewElem = 0;
3762     /*if(!elem->IsPoly())*/ {
3763       switch ( baseType ) {
3764       case SMDSEntity_0D:
3765       case SMDSEntity_Node: { // sweep NODE
3766         if ( nbSame == 0 ) {
3767           if ( isSingleNode[0] )
3768             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3769           else
3770             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3771         }
3772         else
3773           return;
3774         break;
3775       }
3776       case SMDSEntity_Edge: { // sweep EDGE
3777         if ( nbDouble == 0 )
3778         {
3779           if ( nbSame == 0 ) // ---> quadrangle
3780             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3781                                       nextNod[ 1 ], nextNod[ 0 ] );
3782           else               // ---> triangle
3783             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3784                                       nextNod[ iNotSameNode ] );
3785         }
3786         else                 // ---> polygon
3787         {
3788           vector<const SMDS_MeshNode*> poly_nodes;
3789           poly_nodes.push_back( prevNod[0] );
3790           poly_nodes.push_back( prevNod[1] );
3791           if ( prevNod[1] != nextNod[1] )
3792           {
3793             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3794             poly_nodes.push_back( nextNod[1] );
3795           }
3796           if ( prevNod[0] != nextNod[0] )
3797           {
3798             poly_nodes.push_back( nextNod[0] );
3799             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3800           }
3801           switch ( poly_nodes.size() ) {
3802           case 3:
3803             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3804             break;
3805           case 4:
3806             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3807                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3808             break;
3809           default:
3810             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3811           }
3812         }
3813         break;
3814       }
3815       case SMDSEntity_Triangle: // TRIANGLE --->
3816         {
3817           if ( nbDouble > 0 ) break;
3818           if ( nbSame == 0 )       // ---> pentahedron
3819             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3820                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3821
3822           else if ( nbSame == 1 )  // ---> pyramid
3823             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3824                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3825                                          nextNod[ iSameNode ]);
3826
3827           else // 2 same nodes:       ---> tetrahedron
3828             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3829                                          nextNod[ iNotSameNode ]);
3830           break;
3831         }
3832       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3833         {
3834           if ( nbSame == 2 )
3835             return;
3836           if ( nbDouble+nbSame == 2 )
3837           {
3838             if(nbSame==0) {      // ---> quadratic quadrangle
3839               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3840                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3841             }
3842             else { //(nbSame==1) // ---> quadratic triangle
3843               if(sames[0]==2) {
3844                 return; // medium node on axis
3845               }
3846               else if(sames[0]==0)
3847                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3848                                           nextNod[2], midlNod[1], prevNod[2]);
3849               else // sames[0]==1
3850                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3851                                           midlNod[0], nextNod[2], prevNod[2]);
3852             }
3853           }
3854           else if ( nbDouble == 3 )
3855           {
3856             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3857               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3858                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3859             }
3860           }
3861           else
3862             return;
3863           break;
3864         }
3865       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3866         if ( nbDouble > 0 ) break;
3867
3868         if ( nbSame == 0 )       // ---> hexahedron
3869           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3870                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3871
3872         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3873           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3874                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3875                                        nextNod[ iSameNode ]);
3876           newElems.push_back( aNewElem );
3877           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3878                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3879                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3880         }
3881         else if ( nbSame == 2 ) { // ---> pentahedron
3882           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3883             // iBeforeSame is same too
3884             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3885                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3886                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3887           else
3888             // iAfterSame is same too
3889             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3890                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3891                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3892         }
3893         break;
3894       }
3895       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3896         if ( nbDouble+nbSame != 3 ) break;
3897         if(nbSame==0) {
3898           // --->  pentahedron with 15 nodes
3899           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3900                                        nextNod[0], nextNod[1], nextNod[2],
3901                                        prevNod[3], prevNod[4], prevNod[5],
3902                                        nextNod[3], nextNod[4], nextNod[5],
3903                                        midlNod[0], midlNod[1], midlNod[2]);
3904         }
3905         else if(nbSame==1) {
3906           // --->  2d order pyramid of 13 nodes
3907           int apex = iSameNode;
3908           int i0 = ( apex + 1 ) % nbCorners;
3909           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3910           int i0a = apex + 3;
3911           int i1a = i1 + 3;
3912           int i01 = i0 + 3;
3913           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3914                                       nextNod[i0], nextNod[i1], prevNod[apex],
3915                                       prevNod[i01], midlNod[i0],
3916                                       nextNod[i01], midlNod[i1],
3917                                       prevNod[i1a], prevNod[i0a],
3918                                       nextNod[i0a], nextNod[i1a]);
3919         }
3920         else if(nbSame==2) {
3921           // --->  2d order tetrahedron of 10 nodes
3922           int n1 = iNotSameNode;
3923           int n2 = ( n1 + 1             ) % nbCorners;
3924           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3925           int n12 = n1 + 3;
3926           int n23 = n2 + 3;
3927           int n31 = n3 + 3;
3928           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3929                                        prevNod[n12], prevNod[n23], prevNod[n31],
3930                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3931         }
3932         break;
3933       }
3934       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3935         if( nbSame == 0 ) {
3936           if ( nbDouble != 4 ) break;
3937           // --->  hexahedron with 20 nodes
3938           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3939                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3940                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3941                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3942                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3943         }
3944         else if(nbSame==1) {
3945           // ---> pyramid + pentahedron - can not be created since it is needed
3946           // additional middle node at the center of face
3947           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3948           return;
3949         }
3950         else if( nbSame == 2 ) {
3951           if ( nbDouble != 2 ) break;
3952           // --->  2d order Pentahedron with 15 nodes
3953           int n1,n2,n4,n5;
3954           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3955             // iBeforeSame is same too
3956             n1 = iBeforeSame;
3957             n2 = iOpposSame;
3958             n4 = iSameNode;
3959             n5 = iAfterSame;
3960           }
3961           else {
3962             // iAfterSame is same too
3963             n1 = iSameNode;
3964             n2 = iBeforeSame;
3965             n4 = iAfterSame;
3966             n5 = iOpposSame;
3967           }
3968           int n12 = n2 + 4;
3969           int n45 = n4 + 4;
3970           int n14 = n1 + 4;
3971           int n25 = n5 + 4;
3972           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3973                                        prevNod[n4], prevNod[n5], nextNod[n5],
3974                                        prevNod[n12], midlNod[n2], nextNod[n12],
3975                                        prevNod[n45], midlNod[n5], nextNod[n45],
3976                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3977         }
3978         break;
3979       }
3980       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3981
3982         if( nbSame == 0 && nbDouble == 9 ) {
3983           // --->  tri-quadratic hexahedron with 27 nodes
3984           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3985                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3986                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3987                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3988                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3989                                        prevNod[8], // bottom center
3990                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3991                                        nextNod[8], // top center
3992                                        midlNod[8]);// elem center
3993         }
3994         else
3995         {
3996           return;
3997         }
3998         break;
3999       }
4000       case SMDSEntity_Polygon: { // sweep POLYGON
4001
4002         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4003           // --->  hexagonal prism
4004           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4005                                        prevNod[3], prevNod[4], prevNod[5],
4006                                        nextNod[0], nextNod[1], nextNod[2],
4007                                        nextNod[3], nextNod[4], nextNod[5]);
4008         }
4009         break;
4010       }
4011       case SMDSEntity_Ball:
4012         return;
4013
4014       default:
4015         break;
4016       }
4017     }
4018
4019     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4020     {
4021       if ( baseType != SMDSEntity_Polygon )
4022       {
4023         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4024         SMDS_MeshCell::applyInterlace( ind, prevNod );
4025         SMDS_MeshCell::applyInterlace( ind, nextNod );
4026         SMDS_MeshCell::applyInterlace( ind, midlNod );
4027         SMDS_MeshCell::applyInterlace( ind, itNN );
4028         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4029         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4030       }
4031       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4032       vector<int> quantities (nbNodes + 2);
4033       polyedre_nodes.clear();
4034       quantities.clear();
4035
4036       // bottom of prism
4037       for (int inode = 0; inode < nbNodes; inode++)
4038         polyedre_nodes.push_back( prevNod[inode] );
4039       quantities.push_back( nbNodes );
4040
4041       // top of prism
4042       polyedre_nodes.push_back( nextNod[0] );
4043       for (int inode = nbNodes; inode-1; --inode )
4044         polyedre_nodes.push_back( nextNod[inode-1] );
4045       quantities.push_back( nbNodes );
4046
4047       // side faces
4048       for (int iface = 0; iface < nbNodes; iface++)
4049       {
4050         const int prevNbNodes = polyedre_nodes.size();
4051         int inextface = (iface+1) % nbNodes;
4052         polyedre_nodes.push_back( prevNod[inextface] );
4053         polyedre_nodes.push_back( prevNod[iface] );
4054         if ( prevNod[iface] != nextNod[iface] )
4055         {
4056           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4057           polyedre_nodes.push_back( nextNod[iface] );
4058         }
4059         if ( prevNod[inextface] != nextNod[inextface] )
4060         {
4061           polyedre_nodes.push_back( nextNod[inextface] );
4062           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4063         }
4064         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4065         if ( nbFaceNodes > 2 )
4066           quantities.push_back( nbFaceNodes );
4067         else // degenerated face
4068           polyedre_nodes.resize( prevNbNodes );
4069       }
4070       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4071     }
4072
4073     if ( aNewElem ) {
4074       newElems.push_back( aNewElem );
4075       myLastCreatedElems.Append(aNewElem);
4076       srcElements.Append( elem );
4077     }
4078
4079     // set new prev nodes
4080     for ( iNode = 0; iNode < nbNodes; iNode++ )
4081       prevNod[ iNode ] = nextNod[ iNode ];
4082
4083   } // for steps
4084 }
4085
4086 //=======================================================================
4087 /*!
4088  * \brief Create 1D and 2D elements around swept elements
4089  * \param mapNewNodes - source nodes and ones generated from them
4090  * \param newElemsMap - source elements and ones generated from them
4091  * \param elemNewNodesMap - nodes generated from each node of each element
4092  * \param elemSet - all swept elements
4093  * \param nbSteps - number of sweeping steps
4094  * \param srcElements - to append elem for each generated element
4095  */
4096 //=======================================================================
4097
4098 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4099                                   TElemOfElemListMap &     newElemsMap,
4100                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4101                                   TIDSortedElemSet&        elemSet,
4102                                   const int                nbSteps,
4103                                   SMESH_SequenceOfElemPtr& srcElements)
4104 {
4105   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4106   SMESHDS_Mesh* aMesh = GetMeshDS();
4107
4108   // Find nodes belonging to only one initial element - sweep them to get edges.
4109
4110   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4111   for ( ; nList != mapNewNodes.end(); nList++ )
4112   {
4113     const SMDS_MeshNode* node =
4114       static_cast<const SMDS_MeshNode*>( nList->first );
4115     if ( newElemsMap.count( node ))
4116       continue; // node was extruded into edge
4117     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4118     int nbInitElems = 0;
4119     const SMDS_MeshElement* el = 0;
4120     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4121     while ( eIt->more() && nbInitElems < 2 ) {
4122       el = eIt->next();
4123       SMDSAbs_ElementType type = el->GetType();
4124       if ( type == SMDSAbs_Volume || type < highType ) continue;
4125       if ( type > highType ) {
4126         nbInitElems = 0;
4127         highType = type;
4128       }
4129       nbInitElems += elemSet.count(el);
4130     }
4131     if ( nbInitElems < 2 ) {
4132       bool NotCreateEdge = el && el->IsMediumNode(node);
4133       if(!NotCreateEdge) {
4134         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4135         list<const SMDS_MeshElement*> newEdges;
4136         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4137       }
4138     }
4139   }
4140
4141   // Make a ceiling for each element ie an equal element of last new nodes.
4142   // Find free links of faces - make edges and sweep them into faces.
4143
4144   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4145   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4146   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4147   {
4148     const SMDS_MeshElement* elem = itElem->first;
4149     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4150
4151     if(itElem->second.size()==0) continue;
4152
4153     const bool isQuadratic = elem->IsQuadratic();
4154
4155     if ( elem->GetType() == SMDSAbs_Edge ) {
4156       // create a ceiling edge
4157       if ( !isQuadratic ) {
4158         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4159                                vecNewNodes[ 1 ]->second.back())) {
4160           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4161                                                    vecNewNodes[ 1 ]->second.back()));
4162           srcElements.Append( myLastCreatedElems.Last() );
4163         }
4164       }
4165       else {
4166         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4167                                vecNewNodes[ 1 ]->second.back(),
4168                                vecNewNodes[ 2 ]->second.back())) {
4169           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4170                                                    vecNewNodes[ 1 ]->second.back(),
4171                                                    vecNewNodes[ 2 ]->second.back()));
4172           srcElements.Append( myLastCreatedElems.Last() );
4173         }
4174       }
4175     }
4176     if ( elem->GetType() != SMDSAbs_Face )
4177       continue;
4178
4179     bool hasFreeLinks = false;
4180
4181     TIDSortedElemSet avoidSet;
4182     avoidSet.insert( elem );
4183
4184     set<const SMDS_MeshNode*> aFaceLastNodes;
4185     int iNode, nbNodes = vecNewNodes.size();
4186     if ( !isQuadratic ) {
4187       // loop on the face nodes
4188       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4189         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4190         // look for free links of the face
4191         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4192         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4193         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4194         // check if a link is free
4195         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4196           hasFreeLinks = true;
4197           // make an edge and a ceiling for a new edge
4198           if ( !aMesh->FindEdge( n1, n2 )) {
4199             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4200             srcElements.Append( myLastCreatedElems.Last() );
4201           }
4202           n1 = vecNewNodes[ iNode ]->second.back();
4203           n2 = vecNewNodes[ iNext ]->second.back();
4204           if ( !aMesh->FindEdge( n1, n2 )) {
4205             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4206             srcElements.Append( myLastCreatedElems.Last() );
4207           }
4208         }
4209       }
4210     }
4211     else { // elem is quadratic face
4212       int nbn = nbNodes/2;
4213       for ( iNode = 0; iNode < nbn; iNode++ ) {
4214         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4215         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4216         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4217         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4218         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4219         // check if a link is free
4220         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4221              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4222              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4223           hasFreeLinks = true;
4224           // make an edge and a ceiling for a new edge
4225           // find medium node
4226           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4227             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4228             srcElements.Append( myLastCreatedElems.Last() );
4229           }
4230           n1 = vecNewNodes[ iNode ]->second.back();
4231           n2 = vecNewNodes[ iNext ]->second.back();
4232           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4233           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4234             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4235             srcElements.Append( myLastCreatedElems.Last() );
4236           }
4237         }
4238       }
4239       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4240         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4241       }
4242     }
4243
4244     // sweep free links into faces
4245
4246     if ( hasFreeLinks )  {
4247       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4248       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4249
4250       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4251       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4252         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4253         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4254       }
4255       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4256         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4257         std::advance( v, volNb );
4258         // find indices of free faces of a volume and their source edges
4259         list< int > freeInd;
4260         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4261         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4262         int iF, nbF = vTool.NbFaces();
4263         for ( iF = 0; iF < nbF; iF ++ ) {
4264           if (vTool.IsFreeFace( iF ) &&
4265               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4266               initNodeSet != faceNodeSet) // except an initial face
4267           {
4268             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4269               continue;
4270             freeInd.push_back( iF );
4271             // find source edge of a free face iF
4272             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4273             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4274             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4275                                    initNodeSet.begin(), initNodeSet.end(),
4276                                    commonNodes.begin());
4277             if ( (*v)->IsQuadratic() )
4278               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4279             else
4280               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4281 #ifdef _DEBUG_
4282             if ( !srcEdges.back() )
4283             {
4284               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4285                    << iF << " of volume #" << vTool.ID() << endl;
4286             }
4287 #endif
4288           }
4289         }
4290         if ( freeInd.empty() )
4291           continue;
4292
4293         // create faces for all steps;
4294         // if such a face has been already created by sweep of edge,
4295         // assure that its orientation is OK
4296         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4297           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4298           vTool.SetExternalNormal();
4299           const int nextShift = vTool.IsForward() ? +1 : -1;
4300           list< int >::iterator ind = freeInd.begin();
4301           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4302           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4303           {
4304             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4305             int nbn = vTool.NbFaceNodes( *ind );
4306             const SMDS_MeshElement * f = 0;
4307             if ( nbn == 3 )              ///// triangle
4308             {
4309               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4310               if ( !f ||
4311                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4312               {
4313                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4314                                                      nodes[ 1 ],
4315                                                      nodes[ 1 + nextShift ] };
4316                 if ( f )
4317                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4318                 else
4319                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4320                                                             newOrder[ 2 ] ));
4321               }
4322             }
4323             else if ( nbn == 4 )       ///// quadrangle
4324             {
4325               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4326               if ( !f ||
4327                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4328               {
4329                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4330                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4331                 if ( f )
4332                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4333                 else
4334                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4335                                                             newOrder[ 2 ], newOrder[ 3 ]));
4336               }
4337             }
4338             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4339             {
4340               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4341               if ( !f ||
4342                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4343               {
4344                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4345                                                      nodes[2],
4346                                                      nodes[2 + 2*nextShift],
4347                                                      nodes[3 - 2*nextShift],
4348                                                      nodes[3],
4349                                                      nodes[3 + 2*nextShift]};
4350                 if ( f )
4351                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4352                 else
4353                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4354                                                             newOrder[ 1 ],
4355                                                             newOrder[ 2 ],
4356                                                             newOrder[ 3 ],
4357                                                             newOrder[ 4 ],
4358                                                             newOrder[ 5 ] ));
4359               }
4360             }
4361             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4362             {
4363               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4364                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4365               if ( !f ||
4366                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4367               {
4368                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4369                                                      nodes[4 - 2*nextShift],
4370                                                      nodes[4],
4371                                                      nodes[4 + 2*nextShift],
4372                                                      nodes[1],
4373                                                      nodes[5 - 2*nextShift],
4374                                                      nodes[5],
4375                                                      nodes[5 + 2*nextShift] };
4376                 if ( f )
4377                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4378                 else
4379                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4380                                                            newOrder[ 2 ], newOrder[ 3 ],
4381                                                            newOrder[ 4 ], newOrder[ 5 ],
4382                                                            newOrder[ 6 ], newOrder[ 7 ]));
4383               }
4384             }
4385             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4386             {
4387               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4388                                       SMDSAbs_Face, /*noMedium=*/false);
4389               if ( !f ||
4390                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4391               {
4392                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4393                                                      nodes[4 - 2*nextShift],
4394                                                      nodes[4],
4395                                                      nodes[4 + 2*nextShift],
4396                                                      nodes[1],
4397                                                      nodes[5 - 2*nextShift],
4398                                                      nodes[5],
4399                                                      nodes[5 + 2*nextShift],
4400                                                      nodes[8] };
4401                 if ( f )
4402                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4403                 else
4404                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4405                                                            newOrder[ 2 ], newOrder[ 3 ],
4406                                                            newOrder[ 4 ], newOrder[ 5 ],
4407                                                            newOrder[ 6 ], newOrder[ 7 ],
4408                                                            newOrder[ 8 ]));
4409               }
4410             }
4411             else  //////// polygon
4412             {
4413               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4414               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4415               if ( !f ||
4416                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4417               {
4418                 if ( !vTool.IsForward() )
4419                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4420                 if ( f )
4421                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4422                 else
4423                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4424               }
4425             }
4426
4427             while ( srcElements.Length() < myLastCreatedElems.Length() )
4428               srcElements.Append( *srcEdge );
4429
4430           }  // loop on free faces
4431
4432           // go to the next volume
4433           iVol = 0;
4434           while ( iVol++ < nbVolumesByStep ) v++;
4435
4436         } // loop on steps
4437       } // loop on volumes of one step
4438     } // sweep free links into faces
4439
4440     // Make a ceiling face with a normal external to a volume
4441
4442     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4443
4444     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4445     if ( iF >= 0 ) {
4446       lastVol.SetExternalNormal();
4447       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4448       int nbn = lastVol.NbFaceNodes( iF );
4449       if ( nbn == 3 ) {
4450         if (!hasFreeLinks ||
4451             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4452           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4453       }
4454       else if ( nbn == 4 )
4455       {
4456         if (!hasFreeLinks ||
4457             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4458           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4459       }
4460       else if ( nbn == 6 && isQuadratic )
4461       {
4462         if (!hasFreeLinks ||
4463             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4464           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4465                                                    nodes[1], nodes[3], nodes[5]));
4466       }
4467       else if ( nbn == 8 && isQuadratic )
4468       {
4469         if (!hasFreeLinks ||
4470             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4471                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4472           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4473                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4474       }
4475       else if ( nbn == 9 && isQuadratic )
4476       {
4477         if (!hasFreeLinks ||
4478             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4479                                 SMDSAbs_Face, /*noMedium=*/false) )
4480           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4481                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4482                                                    nodes[8]));
4483       }
4484       else {
4485         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4486         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4487           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4488       }
4489
4490       while ( srcElements.Length() < myLastCreatedElems.Length() )
4491         srcElements.Append( myLastCreatedElems.Last() );
4492     }
4493   } // loop on swept elements
4494 }
4495
4496 //=======================================================================
4497 //function : RotationSweep
4498 //purpose  :
4499 //=======================================================================
4500
4501 SMESH_MeshEditor::PGroupIDs
4502 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4503                                 const gp_Ax1&      theAxis,
4504                                 const double       theAngle,
4505                                 const int          theNbSteps,
4506                                 const double       theTol,
4507                                 const bool         theMakeGroups,
4508                                 const bool         theMakeWalls)
4509 {
4510   myLastCreatedElems.Clear();
4511   myLastCreatedNodes.Clear();
4512
4513   // source elements for each generated one
4514   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4515
4516   MESSAGE( "RotationSweep()");
4517   gp_Trsf aTrsf;
4518   aTrsf.SetRotation( theAxis, theAngle );
4519   gp_Trsf aTrsf2;
4520   aTrsf2.SetRotation( theAxis, theAngle/2. );
4521
4522   gp_Lin aLine( theAxis );
4523   double aSqTol = theTol * theTol;
4524
4525   SMESHDS_Mesh* aMesh = GetMeshDS();
4526
4527   TNodeOfNodeListMap mapNewNodes;
4528   TElemOfVecOfNnlmiMap mapElemNewNodes;
4529   TElemOfElemListMap newElemsMap;
4530
4531   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4532                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4533                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4534   // loop on theElems
4535   TIDSortedElemSet::iterator itElem;
4536   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4537     const SMDS_MeshElement* elem = *itElem;
4538     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4539       continue;
4540     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4541     newNodesItVec.reserve( elem->NbNodes() );
4542
4543     // loop on elem nodes
4544     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4545     while ( itN->more() )
4546     {
4547       // check if a node has been already sweeped
4548       const SMDS_MeshNode* node = cast2Node( itN->next() );
4549
4550       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4551       double coord[3];
4552       aXYZ.Coord( coord[0], coord[1], coord[2] );
4553       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4554
4555       TNodeOfNodeListMapItr nIt =
4556         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4557       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4558       if ( listNewNodes.empty() )
4559       {
4560         // check if we are to create medium nodes between corner ones
4561         bool needMediumNodes = false;
4562         if ( isQuadraticMesh )
4563         {
4564           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4565           while (it->more() && !needMediumNodes )
4566           {
4567             const SMDS_MeshElement* invElem = it->next();
4568             if ( invElem != elem && !theElems.count( invElem )) continue;
4569             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4570             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4571               needMediumNodes = true;
4572           }
4573         }
4574
4575         // make new nodes
4576         const SMDS_MeshNode * newNode = node;
4577         for ( int i = 0; i < theNbSteps; i++ ) {
4578           if ( !isOnAxis ) {
4579             if ( needMediumNodes )  // create a medium node
4580             {
4581               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4582               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4583               myLastCreatedNodes.Append(newNode);
4584               srcNodes.Append( node );
4585               listNewNodes.push_back( newNode );
4586               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4587             }
4588             else {
4589               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4590             }
4591             // create a corner node
4592             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4593             myLastCreatedNodes.Append(newNode);
4594             srcNodes.Append( node );
4595             listNewNodes.push_back( newNode );
4596           }
4597           else {
4598             listNewNodes.push_back( newNode );
4599             // if ( needMediumNodes )
4600             //   listNewNodes.push_back( newNode );
4601           }
4602         }
4603       }
4604       newNodesItVec.push_back( nIt );
4605     }
4606     // make new elements
4607     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4608   }
4609
4610   if ( theMakeWalls )
4611     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4612
4613   PGroupIDs newGroupIDs;
4614   if ( theMakeGroups )
4615     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4616
4617   return newGroupIDs;
4618 }
4619
4620
4621 //=======================================================================
4622 //function : CreateNode
4623 //purpose  :
4624 //=======================================================================
4625 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4626                                                   const double y,
4627                                                   const double z,
4628                                                   const double tolnode,
4629                                                   SMESH_SequenceOfNode& aNodes)
4630 {
4631   // myLastCreatedElems.Clear();
4632   // myLastCreatedNodes.Clear();
4633
4634   gp_Pnt P1(x,y,z);
4635   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4636
4637   // try to search in sequence of existing nodes
4638   // if aNodes.Length()>0 we 'nave to use given sequence
4639   // else - use all nodes of mesh
4640   if(aNodes.Length()>0) {
4641     int i;
4642     for(i=1; i<=aNodes.Length(); i++) {
4643       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4644       if(P1.Distance(P2)<tolnode)
4645         return aNodes.Value(i);
4646     }
4647   }
4648   else {
4649     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4650     while(itn->more()) {
4651       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4652       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4653       if(P1.Distance(P2)<tolnode)
4654         return aN;
4655     }
4656   }
4657
4658   // create new node and return it
4659   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4660   //myLastCreatedNodes.Append(NewNode);
4661   return NewNode;
4662 }
4663
4664
4665 //=======================================================================
4666 //function : ExtrusionSweep
4667 //purpose  :
4668 //=======================================================================
4669
4670 SMESH_MeshEditor::PGroupIDs
4671 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4672                                   const gp_Vec&       theStep,
4673                                   const int           theNbSteps,
4674                                   TElemOfElemListMap& newElemsMap,
4675                                   const bool          theMakeGroups,
4676                                   const int           theFlags,
4677                                   const double        theTolerance)
4678 {
4679   ExtrusParam aParams;
4680   aParams.myDir = gp_Dir(theStep);
4681   aParams.myNodes.Clear();
4682   aParams.mySteps = new TColStd_HSequenceOfReal;
4683   int i;
4684   for(i=1; i<=theNbSteps; i++)
4685     aParams.mySteps->Append(theStep.Magnitude());
4686
4687   return
4688     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4689 }
4690
4691
4692 //=======================================================================
4693 //function : ExtrusionSweep
4694 //purpose  :
4695 //=======================================================================
4696
4697 SMESH_MeshEditor::PGroupIDs
4698 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4699                                   ExtrusParam&        theParams,
4700                                   TElemOfElemListMap& newElemsMap,
4701                                   const bool          theMakeGroups,
4702                                   const int           theFlags,
4703                                   const double        theTolerance)
4704 {
4705   myLastCreatedElems.Clear();
4706   myLastCreatedNodes.Clear();
4707
4708   // source elements for each generated one
4709   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4710
4711   SMESHDS_Mesh* aMesh = GetMeshDS();
4712
4713   int nbsteps = theParams.mySteps->Length();
4714
4715   TNodeOfNodeListMap mapNewNodes;
4716   //TNodeOfNodeVecMap mapNewNodes;
4717   TElemOfVecOfNnlmiMap mapElemNewNodes;
4718   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4719
4720   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4721                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4722                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4723   // loop on theElems
4724   TIDSortedElemSet::iterator itElem;
4725   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4726     // check element type
4727     const SMDS_MeshElement* elem = *itElem;
4728     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4729       continue;
4730
4731     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4732     newNodesItVec.reserve( elem->NbNodes() );
4733
4734     // loop on elem nodes
4735     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4736     while ( itN->more() )
4737     {
4738       // check if a node has been already sweeped
4739       const SMDS_MeshNode* node = cast2Node( itN->next() );
4740       TNodeOfNodeListMap::iterator nIt =
4741         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4742       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4743       if ( listNewNodes.empty() )
4744       {
4745         // make new nodes
4746
4747         // check if we are to create medium nodes between corner ones
4748         bool needMediumNodes = false;
4749         if ( isQuadraticMesh )
4750         {
4751           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4752           while (it->more() && !needMediumNodes )
4753           {
4754             const SMDS_MeshElement* invElem = it->next();
4755             if ( invElem != elem && !theElems.count( invElem )) continue;
4756             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4757             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4758               needMediumNodes = true;
4759           }
4760         }
4761
4762         double coord[] = { node->X(), node->Y(), node->Z() };
4763         for ( int i = 0; i < nbsteps; i++ )
4764         {
4765           if ( needMediumNodes ) // create a medium node
4766           {
4767             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4768             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4769             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4770             if( theFlags & EXTRUSION_FLAG_SEW ) {
4771               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4772                                                          theTolerance, theParams.myNodes);
4773               listNewNodes.push_back( newNode );
4774             }
4775             else {
4776               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4777               myLastCreatedNodes.Append(newNode);
4778               srcNodes.Append( node );
4779               listNewNodes.push_back( newNode );
4780             }
4781           }
4782           // create a corner node
4783           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4784           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4785           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4786           if( theFlags & EXTRUSION_FLAG_SEW ) {
4787             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4788                                                        theTolerance, theParams.myNodes);
4789             listNewNodes.push_back( newNode );
4790           }
4791           else {
4792             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4793             myLastCreatedNodes.Append(newNode);
4794             srcNodes.Append( node );
4795             listNewNodes.push_back( newNode );
4796           }
4797         }
4798       }
4799       newNodesItVec.push_back( nIt );
4800     }
4801     // make new elements
4802     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4803   }
4804
4805   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4806     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4807   }
4808   PGroupIDs newGroupIDs;
4809   if ( theMakeGroups )
4810     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4811
4812   return newGroupIDs;
4813 }
4814
4815 //=======================================================================
4816 //function : ExtrusionAlongTrack
4817 //purpose  :
4818 //=======================================================================
4819 SMESH_MeshEditor::Extrusion_Error
4820 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4821                                        SMESH_subMesh*       theTrack,
4822                                        const SMDS_MeshNode* theN1,
4823                                        const bool           theHasAngles,
4824                                        list<double>&        theAngles,
4825                                        const bool           theLinearVariation,
4826                                        const bool           theHasRefPoint,
4827                                        const gp_Pnt&        theRefPoint,
4828                                        const bool           theMakeGroups)
4829 {
4830   MESSAGE("ExtrusionAlongTrack");
4831   myLastCreatedElems.Clear();
4832   myLastCreatedNodes.Clear();
4833
4834   int aNbE;
4835   std::list<double> aPrms;
4836   TIDSortedElemSet::iterator itElem;
4837
4838   gp_XYZ aGC;
4839   TopoDS_Edge aTrackEdge;
4840   TopoDS_Vertex aV1, aV2;
4841
4842   SMDS_ElemIteratorPtr aItE;
4843   SMDS_NodeIteratorPtr aItN;
4844   SMDSAbs_ElementType aTypeE;
4845
4846   TNodeOfNodeListMap mapNewNodes;
4847
4848   // 1. Check data
4849   aNbE = theElements.size();
4850   // nothing to do
4851   if ( !aNbE )
4852     return EXTR_NO_ELEMENTS;
4853
4854   // 1.1 Track Pattern
4855   ASSERT( theTrack );
4856
4857   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4858
4859   aItE = pSubMeshDS->GetElements();
4860   while ( aItE->more() ) {
4861     const SMDS_MeshElement* pE = aItE->next();
4862     aTypeE = pE->GetType();
4863     // Pattern must contain links only
4864     if ( aTypeE != SMDSAbs_Edge )
4865       return EXTR_PATH_NOT_EDGE;
4866   }
4867
4868   list<SMESH_MeshEditor_PathPoint> fullList;
4869
4870   const TopoDS_Shape& aS = theTrack->GetSubShape();
4871   // Sub-shape for the Pattern must be an Edge or Wire
4872   if( aS.ShapeType() == TopAbs_EDGE ) {
4873     aTrackEdge = TopoDS::Edge( aS );
4874     // the Edge must not be degenerated
4875     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4876       return EXTR_BAD_PATH_SHAPE;
4877     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4878     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4879     const SMDS_MeshNode* aN1 = aItN->next();
4880     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4881     const SMDS_MeshNode* aN2 = aItN->next();
4882     // starting node must be aN1 or aN2
4883     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4884       return EXTR_BAD_STARTING_NODE;
4885     aItN = pSubMeshDS->GetNodes();
4886     while ( aItN->more() ) {
4887       const SMDS_MeshNode* pNode = aItN->next();
4888       const SMDS_EdgePosition* pEPos =
4889         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4890       double aT = pEPos->GetUParameter();
4891       aPrms.push_back( aT );
4892     }
4893     //Extrusion_Error err =
4894     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4895   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4896     list< SMESH_subMesh* > LSM;
4897     TopTools_SequenceOfShape Edges;
4898     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4899     while(itSM->more()) {
4900       SMESH_subMesh* SM = itSM->next();
4901       LSM.push_back(SM);
4902       const TopoDS_Shape& aS = SM->GetSubShape();
4903       Edges.Append(aS);
4904     }
4905     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4906     int startNid = theN1->GetID();
4907     TColStd_MapOfInteger UsedNums;
4908
4909     int NbEdges = Edges.Length();
4910     int i = 1;
4911     for(; i<=NbEdges; i++) {
4912       int k = 0;
4913       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4914       for(; itLSM!=LSM.end(); itLSM++) {
4915         k++;
4916         if(UsedNums.Contains(k)) continue;
4917         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4918         SMESH_subMesh* locTrack = *itLSM;
4919         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4920         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4921         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4922         const SMDS_MeshNode* aN1 = aItN->next();
4923         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4924         const SMDS_MeshNode* aN2 = aItN->next();
4925         // starting node must be aN1 or aN2
4926         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4927         // 2. Collect parameters on the track edge
4928         aPrms.clear();
4929         aItN = locMeshDS->GetNodes();
4930         while ( aItN->more() ) {
4931           const SMDS_MeshNode* pNode = aItN->next();
4932           const SMDS_EdgePosition* pEPos =
4933             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4934           double aT = pEPos->GetUParameter();
4935           aPrms.push_back( aT );
4936         }
4937         list<SMESH_MeshEditor_PathPoint> LPP;
4938         //Extrusion_Error err =
4939         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4940         LLPPs.push_back(LPP);
4941         UsedNums.Add(k);
4942         // update startN for search following egde
4943         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4944         else startNid = aN1->GetID();
4945         break;
4946       }
4947     }
4948     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4949     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4950     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4951     for(; itPP!=firstList.end(); itPP++) {
4952       fullList.push_back( *itPP );
4953     }
4954     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4955     fullList.pop_back();
4956     itLLPP++;
4957     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4958       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4959       itPP = currList.begin();
4960       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4961       gp_Dir D1 = PP1.Tangent();
4962       gp_Dir D2 = PP2.Tangent();
4963       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4964                            (D1.Z()+D2.Z())/2 ) );
4965       PP1.SetTangent(Dnew);
4966       fullList.push_back(PP1);
4967       itPP++;
4968       for(; itPP!=firstList.end(); itPP++) {
4969         fullList.push_back( *itPP );
4970       }
4971       PP1 = fullList.back();
4972       fullList.pop_back();
4973     }
4974     // if wire not closed
4975     fullList.push_back(PP1);
4976     // else ???
4977   }
4978   else {
4979     return EXTR_BAD_PATH_SHAPE;
4980   }
4981
4982   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4983                           theHasRefPoint, theRefPoint, theMakeGroups);
4984 }
4985
4986
4987 //=======================================================================
4988 //function : ExtrusionAlongTrack
4989 //purpose  :
4990 //=======================================================================
4991 SMESH_MeshEditor::Extrusion_Error
4992 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4993                                        SMESH_Mesh*          theTrack,
4994                                        const SMDS_MeshNode* theN1,
4995                                        const bool           theHasAngles,
4996                                        list<double>&        theAngles,
4997                                        const bool           theLinearVariation,
4998                                        const bool           theHasRefPoint,
4999                                        const gp_Pnt&        theRefPoint,
5000                                        const bool           theMakeGroups)
5001 {
5002   myLastCreatedElems.Clear();
5003   myLastCreatedNodes.Clear();
5004
5005   int aNbE;
5006   std::list<double> aPrms;
5007   TIDSortedElemSet::iterator itElem;
5008
5009   gp_XYZ aGC;
5010   TopoDS_Edge aTrackEdge;
5011   TopoDS_Vertex aV1, aV2;
5012
5013   SMDS_ElemIteratorPtr aItE;
5014   SMDS_NodeIteratorPtr aItN;
5015   SMDSAbs_ElementType aTypeE;
5016
5017   TNodeOfNodeListMap mapNewNodes;
5018
5019   // 1. Check data
5020   aNbE = theElements.size();
5021   // nothing to do
5022   if ( !aNbE )
5023     return EXTR_NO_ELEMENTS;
5024
5025   // 1.1 Track Pattern
5026   ASSERT( theTrack );
5027
5028   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5029
5030   aItE = pMeshDS->elementsIterator();
5031   while ( aItE->more() ) {
5032     const SMDS_MeshElement* pE = aItE->next();
5033     aTypeE = pE->GetType();
5034     // Pattern must contain links only
5035     if ( aTypeE != SMDSAbs_Edge )
5036       return EXTR_PATH_NOT_EDGE;
5037   }
5038
5039   list<SMESH_MeshEditor_PathPoint> fullList;
5040
5041   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5042
5043   if( aS == SMESH_Mesh::PseudoShape() ) {
5044     //Mesh without shape
5045     const SMDS_MeshNode* currentNode = NULL;
5046     const SMDS_MeshNode* prevNode = theN1;
5047     std::vector<const SMDS_MeshNode*> aNodesList;
5048     aNodesList.push_back(theN1);
5049     int nbEdges = 0, conn=0;
5050     const SMDS_MeshElement* prevElem = NULL;
5051     const SMDS_MeshElement* currentElem = NULL;
5052     int totalNbEdges = theTrack->NbEdges();
5053     SMDS_ElemIteratorPtr nIt;
5054
5055     //check start node
5056     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5057       return EXTR_BAD_STARTING_NODE;
5058     }
5059
5060     conn = nbEdgeConnectivity(theN1);
5061     if(conn > 2)
5062       return EXTR_PATH_NOT_EDGE;
5063
5064     aItE = theN1->GetInverseElementIterator();
5065     prevElem = aItE->next();
5066     currentElem = prevElem;
5067     //Get all nodes
5068     if(totalNbEdges == 1 ) {
5069       nIt = currentElem->nodesIterator();
5070       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5071       if(currentNode == prevNode)
5072         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5073       aNodesList.push_back(currentNode);
5074     } else {
5075       nIt = currentElem->nodesIterator();
5076       while( nIt->more() ) {
5077         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5078         if(currentNode == prevNode)
5079           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5080         aNodesList.push_back(currentNode);
5081
5082         //case of the closed mesh
5083         if(currentNode == theN1) {
5084           nbEdges++;
5085           break;
5086         }
5087
5088         conn = nbEdgeConnectivity(currentNode);
5089         if(conn > 2) {
5090           return EXTR_PATH_NOT_EDGE;
5091         }else if( conn == 1 && nbEdges > 0 ) {
5092           //End of the path
5093           nbEdges++;
5094           break;
5095         }else {
5096           prevNode = currentNode;
5097           aItE = currentNode->GetInverseElementIterator();
5098           currentElem = aItE->next();
5099           if( currentElem  == prevElem)
5100             currentElem = aItE->next();
5101           nIt = currentElem->nodesIterator();
5102           prevElem = currentElem;
5103           nbEdges++;
5104         }
5105       }
5106     }
5107
5108     if(nbEdges != totalNbEdges)
5109       return EXTR_PATH_NOT_EDGE;
5110
5111     TopTools_SequenceOfShape Edges;
5112     double x1,x2,y1,y2,z1,z2;
5113     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5114     int startNid = theN1->GetID();
5115     for(int i = 1; i < aNodesList.size(); i++) {
5116       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5117       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5118       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5119       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5120       list<SMESH_MeshEditor_PathPoint> LPP;
5121       aPrms.clear();
5122       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5123       LLPPs.push_back(LPP);
5124       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5125       else startNid = aNodesList[i-1]->GetID();
5126
5127     }
5128
5129     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5130     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5131     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5132     for(; itPP!=firstList.end(); itPP++) {
5133       fullList.push_back( *itPP );
5134     }
5135
5136     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5137     SMESH_MeshEditor_PathPoint PP2;
5138     fullList.pop_back();
5139     itLLPP++;
5140     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5141       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5142       itPP = currList.begin();
5143       PP2 = currList.front();
5144       gp_Dir D1 = PP1.Tangent();
5145       gp_Dir D2 = PP2.Tangent();
5146       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5147                            (D1.Z()+D2.Z())/2 ) );
5148       PP1.SetTangent(Dnew);
5149       fullList.push_back(PP1);
5150       itPP++;
5151       for(; itPP!=currList.end(); itPP++) {
5152         fullList.push_back( *itPP );
5153       }
5154       PP1 = fullList.back();
5155       fullList.pop_back();
5156     }
5157     fullList.push_back(PP1);
5158
5159   } // Sub-shape for the Pattern must be an Edge or Wire
5160   else if( aS.ShapeType() == TopAbs_EDGE ) {
5161     aTrackEdge = TopoDS::Edge( aS );
5162     // the Edge must not be degenerated
5163     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5164       return EXTR_BAD_PATH_SHAPE;
5165     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5166     aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5167     const SMDS_MeshNode* aN1 = aItN->next();
5168     aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5169     const SMDS_MeshNode* aN2 = aItN->next();
5170     // starting node must be aN1 or aN2
5171     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5172       return EXTR_BAD_STARTING_NODE;
5173     aItN = pMeshDS->nodesIterator();
5174     while ( aItN->more() ) {
5175       const SMDS_MeshNode* pNode = aItN->next();
5176       if( pNode==aN1 || pNode==aN2 ) continue;
5177       const SMDS_EdgePosition* pEPos =
5178         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5179       double aT = pEPos->GetUParameter();
5180       aPrms.push_back( aT );
5181     }
5182     //Extrusion_Error err =
5183     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5184   }
5185   else if( aS.ShapeType() == TopAbs_WIRE ) {
5186     list< SMESH_subMesh* > LSM;
5187     TopTools_SequenceOfShape Edges;
5188     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5189     for(; eExp.More(); eExp.Next()) {
5190       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5191       if( BRep_Tool::Degenerated(E) ) continue;
5192       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5193       if(SM) {
5194         LSM.push_back(SM);
5195         Edges.Append(E);
5196       }
5197     }
5198     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5199     int startNid = theN1->GetID();
5200     TColStd_MapOfInteger UsedNums;
5201     int NbEdges = Edges.Length();
5202     int i = 1;
5203     for(; i<=NbEdges; i++) {
5204       int k = 0;
5205       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5206       for(; itLSM!=LSM.end(); itLSM++) {
5207         k++;
5208         if(UsedNums.Contains(k)) continue;
5209         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5210         SMESH_subMesh* locTrack = *itLSM;
5211         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5212         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5213         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5214         const SMDS_MeshNode* aN1 = aItN->next();
5215         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5216         const SMDS_MeshNode* aN2 = aItN->next();
5217         // starting node must be aN1 or aN2
5218         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5219         // 2. Collect parameters on the track edge
5220         aPrms.clear();
5221         aItN = locMeshDS->GetNodes();
5222         while ( aItN->more() ) {
5223           const SMDS_MeshNode* pNode = aItN->next();
5224           const SMDS_EdgePosition* pEPos =
5225             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5226           double aT = pEPos->GetUParameter();
5227           aPrms.push_back( aT );
5228         }
5229         list<SMESH_MeshEditor_PathPoint> LPP;
5230         //Extrusion_Error err =
5231         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5232         LLPPs.push_back(LPP);
5233         UsedNums.Add(k);
5234         // update startN for search following egde
5235         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5236         else startNid = aN1->GetID();
5237         break;
5238       }
5239     }
5240     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5241     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5242     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5243     for(; itPP!=firstList.end(); itPP++) {
5244       fullList.push_back( *itPP );
5245     }
5246     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5247     fullList.pop_back();
5248     itLLPP++;
5249     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5250       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5251       itPP = currList.begin();
5252       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5253       gp_Dir D1 = PP1.Tangent();
5254       gp_Dir D2 = PP2.Tangent();
5255       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5256                            (D1.Z()+D2.Z())/2 ) );
5257       PP1.SetTangent(Dnew);
5258       fullList.push_back(PP1);
5259       itPP++;
5260       for(; itPP!=currList.end(); itPP++) {
5261         fullList.push_back( *itPP );
5262       }
5263       PP1 = fullList.back();
5264       fullList.pop_back();
5265     }
5266     // if wire not closed
5267     fullList.push_back(PP1);
5268     // else ???
5269   }
5270   else {
5271     return EXTR_BAD_PATH_SHAPE;
5272   }
5273
5274   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5275                           theHasRefPoint, theRefPoint, theMakeGroups);
5276 }
5277
5278
5279 //=======================================================================
5280 //function : MakeEdgePathPoints
5281 //purpose  : auxilary for ExtrusionAlongTrack
5282 //=======================================================================
5283 SMESH_MeshEditor::Extrusion_Error
5284 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5285                                      const TopoDS_Edge& aTrackEdge,
5286                                      bool FirstIsStart,
5287                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5288 {
5289   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5290   aTolVec=1.e-7;
5291   aTolVec2=aTolVec*aTolVec;
5292   double aT1, aT2;
5293   TopoDS_Vertex aV1, aV2;
5294   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5295   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5296   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5297   // 2. Collect parameters on the track edge
5298   aPrms.push_front( aT1 );
5299   aPrms.push_back( aT2 );
5300   // sort parameters
5301   aPrms.sort();
5302   if( FirstIsStart ) {
5303     if ( aT1 > aT2 ) {
5304       aPrms.reverse();
5305     }
5306   }
5307   else {
5308     if ( aT2 > aT1 ) {
5309       aPrms.reverse();
5310     }
5311   }
5312   // 3. Path Points
5313   SMESH_MeshEditor_PathPoint aPP;
5314   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5315   std::list<double>::iterator aItD = aPrms.begin();
5316   for(; aItD != aPrms.end(); ++aItD) {
5317     double aT = *aItD;
5318     gp_Pnt aP3D;
5319     gp_Vec aVec;
5320     aC3D->D1( aT, aP3D, aVec );
5321     aL2 = aVec.SquareMagnitude();
5322     if ( aL2 < aTolVec2 )
5323       return EXTR_CANT_GET_TANGENT;
5324     gp_Dir aTgt( aVec );
5325     aPP.SetPnt( aP3D );
5326     aPP.SetTangent( aTgt );
5327     aPP.SetParameter( aT );
5328     LPP.push_back(aPP);
5329   }
5330   return EXTR_OK;
5331 }
5332
5333
5334 //=======================================================================
5335 //function : MakeExtrElements
5336 //purpose  : auxilary for ExtrusionAlongTrack
5337 //=======================================================================
5338 SMESH_MeshEditor::Extrusion_Error
5339 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5340                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5341                                    const bool theHasAngles,
5342                                    list<double>& theAngles,
5343                                    const bool theLinearVariation,
5344                                    const bool theHasRefPoint,
5345                                    const gp_Pnt& theRefPoint,
5346                                    const bool theMakeGroups)
5347 {
5348   MESSAGE("MakeExtrElements");
5349   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5350   int aNbTP = fullList.size();
5351   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5352   // Angles
5353   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5354     LinearAngleVariation(aNbTP-1, theAngles);
5355   }
5356   vector<double> aAngles( aNbTP );
5357   int j = 0;
5358   for(; j<aNbTP; ++j) {
5359     aAngles[j] = 0.;
5360   }
5361   if ( theHasAngles ) {
5362     double anAngle;;
5363     std::list<double>::iterator aItD = theAngles.begin();
5364     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5365       anAngle = *aItD;
5366       aAngles[j] = anAngle;
5367     }
5368   }
5369   // fill vector of path points with angles
5370   //aPPs.resize(fullList.size());
5371   j = -1;
5372   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5373   for(; itPP!=fullList.end(); itPP++) {
5374     j++;
5375     SMESH_MeshEditor_PathPoint PP = *itPP;
5376     PP.SetAngle(aAngles[j]);
5377     aPPs[j] = PP;
5378   }
5379
5380   TNodeOfNodeListMap mapNewNodes;
5381   TElemOfVecOfNnlmiMap mapElemNewNodes;
5382   TElemOfElemListMap newElemsMap;
5383   TIDSortedElemSet::iterator itElem;
5384   double aX, aY, aZ;
5385   int aNb;
5386   SMDSAbs_ElementType aTypeE;
5387   // source elements for each generated one
5388   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5389
5390   // 3. Center of rotation aV0
5391   gp_Pnt aV0 = theRefPoint;
5392   gp_XYZ aGC;
5393   if ( !theHasRefPoint ) {
5394     aNb = 0;
5395     aGC.SetCoord( 0.,0.,0. );
5396
5397     itElem = theElements.begin();
5398     for ( ; itElem != theElements.end(); itElem++ ) {
5399       const SMDS_MeshElement* elem = *itElem;
5400
5401       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5402       while ( itN->more() ) {
5403         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5404         aX = node->X();
5405         aY = node->Y();
5406         aZ = node->Z();
5407
5408         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5409           list<const SMDS_MeshNode*> aLNx;
5410           mapNewNodes[node] = aLNx;
5411           //
5412           gp_XYZ aXYZ( aX, aY, aZ );
5413           aGC += aXYZ;
5414           ++aNb;
5415         }
5416       }
5417     }
5418     aGC /= aNb;
5419     aV0.SetXYZ( aGC );
5420   } // if (!theHasRefPoint) {
5421   mapNewNodes.clear();
5422
5423   // 4. Processing the elements
5424   SMESHDS_Mesh* aMesh = GetMeshDS();
5425
5426   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5427     // check element type
5428     const SMDS_MeshElement* elem = *itElem;
5429     aTypeE = elem->GetType();
5430     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5431       continue;
5432
5433     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5434     newNodesItVec.reserve( elem->NbNodes() );
5435
5436     // loop on elem nodes
5437     int nodeIndex = -1;
5438     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5439     while ( itN->more() )
5440     {
5441       ++nodeIndex;
5442       // check if a node has been already processed
5443       const SMDS_MeshNode* node =
5444         static_cast<const SMDS_MeshNode*>( itN->next() );
5445       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5446       if ( nIt == mapNewNodes.end() ) {
5447         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5448         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5449
5450         // make new nodes
5451         aX = node->X();  aY = node->Y(); aZ = node->Z();
5452
5453         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5454         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5455         gp_Ax1 anAx1, anAxT1T0;
5456         gp_Dir aDT1x, aDT0x, aDT1T0;
5457
5458         aTolAng=1.e-4;
5459
5460         aV0x = aV0;
5461         aPN0.SetCoord(aX, aY, aZ);
5462
5463         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5464         aP0x = aPP0.Pnt();
5465         aDT0x= aPP0.Tangent();
5466         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5467
5468         for ( j = 1; j < aNbTP; ++j ) {
5469           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5470           aP1x = aPP1.Pnt();
5471           aDT1x = aPP1.Tangent();
5472           aAngle1x = aPP1.Angle();
5473
5474           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5475           // Translation
5476           gp_Vec aV01x( aP0x, aP1x );
5477           aTrsf.SetTranslation( aV01x );
5478
5479           // traslated point
5480           aV1x = aV0x.Transformed( aTrsf );
5481           aPN1 = aPN0.Transformed( aTrsf );
5482
5483           // rotation 1 [ T1,T0 ]
5484           aAngleT1T0=-aDT1x.Angle( aDT0x );
5485           if (fabs(aAngleT1T0) > aTolAng) {
5486             aDT1T0=aDT1x^aDT0x;
5487             anAxT1T0.SetLocation( aV1x );
5488             anAxT1T0.SetDirection( aDT1T0 );
5489             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5490
5491             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5492           }
5493
5494           // rotation 2
5495           if ( theHasAngles ) {
5496             anAx1.SetLocation( aV1x );
5497             anAx1.SetDirection( aDT1x );
5498             aTrsfRot.SetRotation( anAx1, aAngle1x );
5499
5500             aPN1 = aPN1.Transformed( aTrsfRot );
5501           }
5502
5503           // make new node
5504           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5505           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5506             // create additional node
5507             double x = ( aPN1.X() + aPN0.X() )/2.;
5508             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5509             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5510             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5511             myLastCreatedNodes.Append(newNode);
5512             srcNodes.Append( node );
5513             listNewNodes.push_back( newNode );
5514           }
5515           aX = aPN1.X();
5516           aY = aPN1.Y();
5517           aZ = aPN1.Z();
5518           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5519           myLastCreatedNodes.Append(newNode);
5520           srcNodes.Append( node );
5521           listNewNodes.push_back( newNode );
5522
5523           aPN0 = aPN1;
5524           aP0x = aP1x;
5525           aV0x = aV1x;
5526           aDT0x = aDT1x;
5527         }
5528       }
5529
5530       else {
5531         // if current elem is quadratic and current node is not medium
5532         // we have to check - may be it is needed to insert additional nodes
5533         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5534           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5535           if(listNewNodes.size()==aNbTP-1) {
5536             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5537             gp_XYZ P(node->X(), node->Y(), node->Z());
5538             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5539             int i;
5540             for(i=0; i<aNbTP-1; i++) {
5541               const SMDS_MeshNode* N = *it;
5542               double x = ( N->X() + P.X() )/2.;
5543               double y = ( N->Y() + P.Y() )/2.;
5544               double z = ( N->Z() + P.Z() )/2.;
5545               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5546               srcNodes.Append( node );
5547               myLastCreatedNodes.Append(newN);
5548               aNodes[2*i] = newN;
5549               aNodes[2*i+1] = N;
5550               P = gp_XYZ(N->X(),N->Y(),N->Z());
5551             }
5552             listNewNodes.clear();
5553             for(i=0; i<2*(aNbTP-1); i++) {
5554               listNewNodes.push_back(aNodes[i]);
5555             }
5556           }
5557         }
5558       }
5559
5560       newNodesItVec.push_back( nIt );
5561     }
5562     // make new elements
5563     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5564     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5565     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5566   }
5567
5568   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5569
5570   if ( theMakeGroups )
5571     generateGroups( srcNodes, srcElems, "extruded");
5572
5573   return EXTR_OK;
5574 }
5575
5576
5577 //=======================================================================
5578 //function : LinearAngleVariation
5579 //purpose  : auxilary for ExtrusionAlongTrack
5580 //=======================================================================
5581 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5582                                             list<double>& Angles)
5583 {
5584   int nbAngles = Angles.size();
5585   if( nbSteps > nbAngles ) {
5586     vector<double> theAngles(nbAngles);
5587     list<double>::iterator it = Angles.begin();
5588     int i = -1;
5589     for(; it!=Angles.end(); it++) {
5590       i++;
5591       theAngles[i] = (*it);
5592     }
5593     list<double> res;
5594     double rAn2St = double( nbAngles ) / double( nbSteps );
5595     double angPrev = 0, angle;
5596     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5597       double angCur = rAn2St * ( iSt+1 );
5598       double angCurFloor  = floor( angCur );
5599       double angPrevFloor = floor( angPrev );
5600       if ( angPrevFloor == angCurFloor )
5601         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5602       else {
5603         int iP = int( angPrevFloor );
5604         double angPrevCeil = ceil(angPrev);
5605         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5606
5607         int iC = int( angCurFloor );
5608         if ( iC < nbAngles )
5609           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5610
5611         iP = int( angPrevCeil );
5612         while ( iC-- > iP )
5613           angle += theAngles[ iC ];
5614       }
5615       res.push_back(angle);
5616       angPrev = angCur;
5617     }
5618     Angles.clear();
5619     it = res.begin();
5620     for(; it!=res.end(); it++)
5621       Angles.push_back( *it );
5622   }
5623 }
5624
5625
5626 //================================================================================
5627 /*!
5628  * \brief Move or copy theElements applying theTrsf to their nodes
5629  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5630  *  \param theTrsf - transformation to apply
5631  *  \param theCopy - if true, create translated copies of theElems
5632  *  \param theMakeGroups - if true and theCopy, create translated groups
5633  *  \param theTargetMesh - mesh to copy translated elements into
5634  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5635  */
5636 //================================================================================
5637
5638 SMESH_MeshEditor::PGroupIDs
5639 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5640                              const gp_Trsf&     theTrsf,
5641                              const bool         theCopy,
5642                              const bool         theMakeGroups,
5643                              SMESH_Mesh*        theTargetMesh)
5644 {
5645   myLastCreatedElems.Clear();
5646   myLastCreatedNodes.Clear();
5647
5648   bool needReverse = false;
5649   string groupPostfix;
5650   switch ( theTrsf.Form() ) {
5651   case gp_PntMirror:
5652     MESSAGE("gp_PntMirror");
5653     needReverse = true;
5654     groupPostfix = "mirrored";
5655     break;
5656   case gp_Ax1Mirror:
5657     MESSAGE("gp_Ax1Mirror");
5658     groupPostfix = "mirrored";
5659     break;
5660   case gp_Ax2Mirror:
5661     MESSAGE("gp_Ax2Mirror");
5662     needReverse = true;
5663     groupPostfix = "mirrored";
5664     break;
5665   case gp_Rotation:
5666     MESSAGE("gp_Rotation");
5667     groupPostfix = "rotated";
5668     break;
5669   case gp_Translation:
5670     MESSAGE("gp_Translation");
5671     groupPostfix = "translated";
5672     break;
5673   case gp_Scale:
5674     MESSAGE("gp_Scale");
5675     groupPostfix = "scaled";
5676     break;
5677   case gp_CompoundTrsf: // different scale by axis
5678     MESSAGE("gp_CompoundTrsf");
5679     groupPostfix = "scaled";
5680     break;
5681   default:
5682     MESSAGE("default");
5683     needReverse = false;
5684     groupPostfix = "transformed";
5685   }
5686
5687   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5688   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5689   SMESHDS_Mesh* aMesh    = GetMeshDS();
5690
5691
5692   // map old node to new one
5693   TNodeNodeMap nodeMap;
5694
5695   // elements sharing moved nodes; those of them which have all
5696   // nodes mirrored but are not in theElems are to be reversed
5697   TIDSortedElemSet inverseElemSet;
5698
5699   // source elements for each generated one
5700   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5701
5702   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5703   TIDSortedElemSet orphanNode;
5704
5705   if ( theElems.empty() ) // transform the whole mesh
5706   {
5707     // add all elements
5708     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5709     while ( eIt->more() ) theElems.insert( eIt->next() );
5710     // add orphan nodes
5711     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5712     while ( nIt->more() )
5713     {
5714       const SMDS_MeshNode* node = nIt->next();
5715       if ( node->NbInverseElements() == 0)
5716         orphanNode.insert( node );
5717     }
5718   }
5719
5720   // loop on elements to transform nodes : first orphan nodes then elems
5721   TIDSortedElemSet::iterator itElem;
5722   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5723   for (int i=0; i<2; i++)
5724   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5725     const SMDS_MeshElement* elem = *itElem;
5726     if ( !elem )
5727       continue;
5728
5729     // loop on elem nodes
5730     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5731     while ( itN->more() ) {
5732
5733       const SMDS_MeshNode* node = cast2Node( itN->next() );
5734       // check if a node has been already transformed
5735       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5736         nodeMap.insert( make_pair ( node, node ));
5737       if ( !n2n_isnew.second )
5738         continue;
5739
5740       double coord[3];
5741       coord[0] = node->X();
5742       coord[1] = node->Y();
5743       coord[2] = node->Z();
5744       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5745       if ( theTargetMesh ) {
5746         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5747         n2n_isnew.first->second = newNode;
5748         myLastCreatedNodes.Append(newNode);
5749         srcNodes.Append( node );
5750       }
5751       else if ( theCopy ) {
5752         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5753         n2n_isnew.first->second = newNode;
5754         myLastCreatedNodes.Append(newNode);
5755         srcNodes.Append( node );
5756       }
5757       else {
5758         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5759         // node position on shape becomes invalid
5760         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5761           ( SMDS_SpacePosition::originSpacePosition() );
5762       }
5763
5764       // keep inverse elements
5765       if ( !theCopy && !theTargetMesh && needReverse ) {
5766         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5767         while ( invElemIt->more() ) {
5768           const SMDS_MeshElement* iel = invElemIt->next();
5769           inverseElemSet.insert( iel );
5770         }
5771       }
5772     }
5773   }
5774
5775   // either create new elements or reverse mirrored ones
5776   if ( !theCopy && !needReverse && !theTargetMesh )
5777     return PGroupIDs();
5778
5779   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5780   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5781     theElems.insert( *invElemIt );
5782
5783   // Replicate or reverse elements
5784
5785   std::vector<int> iForw;
5786   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5787   {
5788     const SMDS_MeshElement* elem = *itElem;
5789     if ( !elem ) continue;
5790
5791     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5792     int                  nbNodes  = elem->NbNodes();
5793     if ( geomType == SMDSGeom_NONE ) continue; // node
5794
5795     switch ( geomType ) {
5796
5797     case SMDSGeom_POLYGON:  // ---------------------- polygon
5798       {
5799         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5800         int iNode = 0;
5801         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5802         while (itN->more()) {
5803           const SMDS_MeshNode* node =
5804             static_cast<const SMDS_MeshNode*>(itN->next());
5805           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5806           if (nodeMapIt == nodeMap.end())
5807             break; // not all nodes transformed
5808           if (needReverse) {
5809             // reverse mirrored faces and volumes
5810             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5811           } else {
5812             poly_nodes[iNode] = (*nodeMapIt).second;
5813           }
5814           iNode++;
5815         }
5816         if ( iNode != nbNodes )
5817           continue; // not all nodes transformed
5818
5819         if ( theTargetMesh ) {
5820           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5821           srcElems.Append( elem );
5822         }
5823         else if ( theCopy ) {
5824           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5825           srcElems.Append( elem );
5826         }
5827         else {
5828           aMesh->ChangePolygonNodes(elem, poly_nodes);
5829         }
5830       }
5831       break;
5832
5833     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5834       {
5835         const SMDS_VtkVolume* aPolyedre =
5836           dynamic_cast<const SMDS_VtkVolume*>( elem );
5837         if (!aPolyedre) {
5838           MESSAGE("Warning: bad volumic element");
5839           continue;
5840         }
5841
5842         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5843         vector<int> quantities; quantities.reserve( nbNodes );
5844
5845         bool allTransformed = true;
5846         int nbFaces = aPolyedre->NbFaces();
5847         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5848           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5849           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5850             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5851             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5852             if (nodeMapIt == nodeMap.end()) {
5853               allTransformed = false; // not all nodes transformed
5854             } else {
5855               poly_nodes.push_back((*nodeMapIt).second);
5856             }
5857             if ( needReverse && allTransformed )
5858               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5859           }
5860           quantities.push_back(nbFaceNodes);
5861         }
5862         if ( !allTransformed )
5863           continue; // not all nodes transformed
5864
5865         if ( theTargetMesh ) {
5866           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5867           srcElems.Append( elem );
5868         }
5869         else if ( theCopy ) {
5870           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5871           srcElems.Append( elem );
5872         }
5873         else {
5874           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5875         }
5876       }
5877       break;
5878
5879     case SMDSGeom_BALL: // -------------------- Ball
5880       {
5881         if ( !theCopy && !theTargetMesh ) continue;
5882
5883         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5884         if (nodeMapIt == nodeMap.end())
5885           continue; // not all nodes transformed
5886
5887         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5888         if ( theTargetMesh ) {
5889           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5890           srcElems.Append( elem );
5891         }
5892         else {
5893           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5894           srcElems.Append( elem );
5895         }
5896       }
5897       break;
5898
5899     default: // ----------------------- Regular elements
5900
5901       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5902       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5903       const std::vector<int>& i = needReverse ? iRev : iForw;
5904
5905       // find transformed nodes
5906       vector<const SMDS_MeshNode*> nodes(nbNodes);
5907       int iNode = 0;
5908       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5909       while ( itN->more() ) {
5910         const SMDS_MeshNode* node =
5911           static_cast<const SMDS_MeshNode*>( itN->next() );
5912         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5913         if ( nodeMapIt == nodeMap.end() )
5914           break; // not all nodes transformed
5915         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5916       }
5917       if ( iNode != nbNodes )
5918         continue; // not all nodes transformed
5919
5920       if ( theTargetMesh ) {
5921         if ( SMDS_MeshElement* copy =
5922              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5923           myLastCreatedElems.Append( copy );
5924           srcElems.Append( elem );
5925         }
5926       }
5927       else if ( theCopy ) {
5928         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5929           srcElems.Append( elem );
5930       }
5931       else {
5932         // reverse element as it was reversed by transformation
5933         if ( nbNodes > 2 )
5934           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5935       }
5936     } // switch ( geomType )
5937
5938   } // loop on elements
5939
5940   PGroupIDs newGroupIDs;
5941
5942   if ( ( theMakeGroups && theCopy ) ||
5943        ( theMakeGroups && theTargetMesh ) )
5944     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5945
5946   return newGroupIDs;
5947 }
5948
5949 //=======================================================================
5950 /*!
5951  * \brief Create groups of elements made during transformation
5952  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5953  * \param elemGens - elements making corresponding myLastCreatedElems
5954  * \param postfix - to append to names of new groups
5955  */
5956 //=======================================================================
5957
5958 SMESH_MeshEditor::PGroupIDs
5959 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5960                                  const SMESH_SequenceOfElemPtr& elemGens,
5961                                  const std::string&             postfix,
5962                                  SMESH_Mesh*                    targetMesh)
5963 {
5964   PGroupIDs newGroupIDs( new list<int> );
5965   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5966
5967   // Sort existing groups by types and collect their names
5968
5969   // to store an old group and a generated new one
5970   typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5971   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5972   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5973   // group names
5974   set< string > groupNames;
5975
5976   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5977   if ( !groupIt->more() ) return newGroupIDs;
5978
5979   int newGroupID = mesh->GetGroupIds().back()+1;
5980   while ( groupIt->more() )
5981   {
5982     SMESH_Group * group = groupIt->next();
5983     if ( !group ) continue;
5984     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5985     if ( !groupDS || groupDS->IsEmpty() ) continue;
5986     groupNames.insert( group->GetName() );
5987     groupDS->SetStoreName( group->GetName() );
5988     SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5989                                                  groupDS->GetType() );
5990     groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5991     orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5992   }
5993
5994   // Loop on nodes and elements to add them in new groups
5995
5996   for ( int isNodes = 0; isNodes < 2; ++isNodes )
5997   {
5998     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
5999     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6000     if ( gens.Length() != elems.Length() )
6001       throw SALOME_Exception(LOCALIZED("invalid args"));
6002
6003     // loop on created elements
6004     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6005     {
6006       const SMDS_MeshElement* sourceElem = gens( iElem );
6007       if ( !sourceElem ) {
6008         MESSAGE("generateGroups(): NULL source element");
6009         continue;
6010       }
6011       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6012       if ( groupsOldNew.empty() ) { // no groups of this type at all
6013         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6014           ++iElem; // skip all elements made by sourceElem
6015         continue;
6016       }
6017       // collect all elements made by sourceElem
6018       list< const SMDS_MeshElement* > resultElems;
6019       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6020         if ( resElem != sourceElem )
6021           resultElems.push_back( resElem );
6022       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6023         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6024           if ( resElem != sourceElem )
6025             resultElems.push_back( resElem );
6026
6027       // add resultElems to groups made by ones the sourceElem belongs to
6028       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6029       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6030       {
6031         SMESHDS_GroupBase* oldGroup = gOldNew->first;
6032         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6033         {
6034           // fill in a new group
6035           SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6036           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6037           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6038             newGroup.Add( *resElemIt );
6039         }
6040       }
6041     } // loop on created elements
6042   }// loop on nodes and elements
6043
6044   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6045
6046   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6047   {
6048     SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6049     SMESHDS_Group*     newGroupDS = orderedOldNewGroups[i]->second;
6050     if ( newGroupDS->IsEmpty() )
6051     {
6052       mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6053     }
6054     else
6055     {
6056       // make a name
6057       string name = oldGroupDS->GetStoreName();
6058       if ( !targetMesh ) {
6059         name += "_";
6060         name += postfix;
6061         int nb = 1;
6062         while ( !groupNames.insert( name ).second ) // name exists
6063           name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6064       }
6065       newGroupDS->SetStoreName( name.c_str() );
6066
6067       // make a SMESH_Groups
6068       mesh->AddGroup( newGroupDS );
6069       newGroupIDs->push_back( newGroupDS->GetID() );
6070
6071       // set group type
6072       newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6073     }
6074   }
6075
6076   return newGroupIDs;
6077 }
6078
6079 //================================================================================
6080 /*!
6081  * \brief Return list of group of nodes close to each other within theTolerance
6082  *        Search among theNodes or in the whole mesh if theNodes is empty using
6083  *        an Octree algorithm
6084  */
6085 //================================================================================
6086
6087 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6088                                             const double         theTolerance,
6089                                             TListOfListOfNodes & theGroupsOfNodes)
6090 {
6091   myLastCreatedElems.Clear();
6092   myLastCreatedNodes.Clear();
6093
6094   if ( theNodes.empty() )
6095   { // get all nodes in the mesh
6096     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6097     while ( nIt->more() )
6098       theNodes.insert( theNodes.end(),nIt->next());
6099   }
6100
6101   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6102 }
6103
6104
6105 //=======================================================================
6106 /*!
6107  * \brief Implementation of search for the node closest to point
6108  */
6109 //=======================================================================
6110
6111 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6112 {
6113   //---------------------------------------------------------------------
6114   /*!
6115    * \brief Constructor
6116    */
6117   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6118   {
6119     myMesh = ( SMESHDS_Mesh* ) theMesh;
6120
6121     TIDSortedNodeSet nodes;
6122     if ( theMesh ) {
6123       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6124       while ( nIt->more() )
6125         nodes.insert( nodes.end(), nIt->next() );
6126     }
6127     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6128
6129     // get max size of a leaf box
6130     SMESH_OctreeNode* tree = myOctreeNode;
6131     while ( !tree->isLeaf() )
6132     {
6133       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6134       if ( cIt->more() )
6135         tree = cIt->next();
6136     }
6137     myHalfLeafSize = tree->maxSize() / 2.;
6138   }
6139
6140   //---------------------------------------------------------------------
6141   /*!
6142    * \brief Move node and update myOctreeNode accordingly
6143    */
6144   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6145   {
6146     myOctreeNode->UpdateByMoveNode( node, toPnt );
6147     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6148   }
6149
6150   //---------------------------------------------------------------------
6151   /*!
6152    * \brief Do it's job
6153    */
6154   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6155   {
6156     map<double, const SMDS_MeshNode*> dist2Nodes;
6157     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6158     if ( !dist2Nodes.empty() )
6159       return dist2Nodes.begin()->second;
6160     list<const SMDS_MeshNode*> nodes;
6161     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6162
6163     double minSqDist = DBL_MAX;
6164     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6165     {
6166       // sort leafs by their distance from thePnt
6167       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6168       TDistTreeMap treeMap;
6169       list< SMESH_OctreeNode* > treeList;
6170       list< SMESH_OctreeNode* >::iterator trIt;
6171       treeList.push_back( myOctreeNode );
6172
6173       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6174       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6175       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6176       {
6177         SMESH_OctreeNode* tree = *trIt;
6178         if ( !tree->isLeaf() ) // put children to the queue
6179         {
6180           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6181           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6182           while ( cIt->more() )
6183             treeList.push_back( cIt->next() );
6184         }
6185         else if ( tree->NbNodes() ) // put a tree to the treeMap
6186         {
6187           const Bnd_B3d& box = *tree->getBox();
6188           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6189           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6190           if ( !it_in.second ) // not unique distance to box center
6191             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6192         }
6193       }
6194       // find distance after which there is no sense to check tree's
6195       double sqLimit = DBL_MAX;
6196       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6197       if ( treeMap.size() > 5 ) {
6198         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6199         const Bnd_B3d& box = *closestTree->getBox();
6200         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6201         sqLimit = limit * limit;
6202       }
6203       // get all nodes from trees
6204       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6205         if ( sqDist_tree->first > sqLimit )
6206           break;
6207         SMESH_OctreeNode* tree = sqDist_tree->second;
6208         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6209       }
6210     }
6211     // find closest among nodes
6212     minSqDist = DBL_MAX;
6213     const SMDS_MeshNode* closestNode = 0;
6214     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6215     for ( ; nIt != nodes.end(); ++nIt ) {
6216       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6217       if ( minSqDist > sqDist ) {
6218         closestNode = *nIt;
6219         minSqDist = sqDist;
6220       }
6221     }
6222     return closestNode;
6223   }
6224
6225   //---------------------------------------------------------------------
6226   /*!
6227    * \brief Destructor
6228    */
6229   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6230
6231   //---------------------------------------------------------------------
6232   /*!
6233    * \brief Return the node tree
6234    */
6235   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6236
6237 private:
6238   SMESH_OctreeNode* myOctreeNode;
6239   SMESHDS_Mesh*     myMesh;
6240   double            myHalfLeafSize; // max size of a leaf box
6241 };
6242
6243 //=======================================================================
6244 /*!
6245  * \brief Return SMESH_NodeSearcher
6246  */
6247 //=======================================================================
6248
6249 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6250 {
6251   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6252 }
6253
6254 // ========================================================================
6255 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6256 {
6257   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6258   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6259   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6260
6261   //=======================================================================
6262   /*!
6263    * \brief Octal tree of bounding boxes of elements
6264    */
6265   //=======================================================================
6266
6267   class ElementBndBoxTree : public SMESH_Octree
6268   {
6269   public:
6270
6271     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6272                       SMDSAbs_ElementType  elemType,
6273                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6274                       double               tolerance = NodeRadius );
6275     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6276     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6277     void getElementsInSphere ( const gp_XYZ& center,
6278                                const double  radius, TIDSortedElemSet& foundElems);
6279     size_t getSize() { return std::max( _size, _elements.size() ); }
6280     ~ElementBndBoxTree();
6281
6282   protected:
6283     ElementBndBoxTree():_size(0) {}
6284     SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6285     void          buildChildrenData();
6286     Bnd_B3d*      buildRootBox();
6287   private:
6288     //!< Bounding box of element
6289     struct ElementBox : public Bnd_B3d
6290     {
6291       const SMDS_MeshElement* _element;
6292       int                     _refCount; // an ElementBox can be included in several tree branches
6293       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6294     };
6295     vector< ElementBox* > _elements;
6296     size_t                _size;
6297   };
6298
6299   //================================================================================
6300   /*!
6301    * \brief ElementBndBoxTree creation
6302    */
6303   //================================================================================
6304
6305   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6306     :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6307   {
6308     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6309     _elements.reserve( nbElems );
6310
6311     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6312     while ( elemIt->more() )
6313       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6314
6315     compute();
6316   }
6317
6318   //================================================================================
6319   /*!
6320    * \brief Destructor
6321    */
6322   //================================================================================
6323
6324   ElementBndBoxTree::~ElementBndBoxTree()
6325   {
6326     for ( int i = 0; i < _elements.size(); ++i )
6327       if ( --_elements[i]->_refCount <= 0 )
6328         delete _elements[i];
6329   }
6330
6331   //================================================================================
6332   /*!
6333    * \brief Return the maximal box
6334    */
6335   //================================================================================
6336
6337   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6338   {
6339     Bnd_B3d* box = new Bnd_B3d;
6340     for ( int i = 0; i < _elements.size(); ++i )
6341       box->Add( *_elements[i] );
6342     return box;
6343   }
6344
6345   //================================================================================
6346   /*!
6347    * \brief Redistrubute element boxes among children
6348    */
6349   //================================================================================
6350
6351   void ElementBndBoxTree::buildChildrenData()
6352   {
6353     for ( int i = 0; i < _elements.size(); ++i )
6354     {
6355       for (int j = 0; j < 8; j++)
6356       {
6357         if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6358         {
6359           _elements[i]->_refCount++;
6360           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6361         }
6362       }
6363       _elements[i]->_refCount--;
6364     }
6365     _size = _elements.size();
6366     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6367
6368     for (int j = 0; j < 8; j++)
6369     {
6370       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6371       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6372         child->myIsLeaf = true;
6373
6374       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6375         SMESHUtils::CompactVector( child->_elements );
6376     }
6377   }
6378
6379   //================================================================================
6380   /*!
6381    * \brief Return elements which can include the point
6382    */
6383   //================================================================================
6384
6385   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6386                                                 TIDSortedElemSet& foundElems)
6387   {
6388     if ( getBox()->IsOut( point.XYZ() ))
6389       return;
6390
6391     if ( isLeaf() )
6392     {
6393       for ( int i = 0; i < _elements.size(); ++i )
6394         if ( !_elements[i]->IsOut( point.XYZ() ))
6395           foundElems.insert( _elements[i]->_element );
6396     }
6397     else
6398     {
6399       for (int i = 0; i < 8; i++)
6400         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6401     }
6402   }
6403
6404   //================================================================================
6405   /*!
6406    * \brief Return elements which can be intersected by the line
6407    */
6408   //================================================================================
6409
6410   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6411                                                TIDSortedElemSet& foundElems)
6412   {
6413     if ( getBox()->IsOut( line ))
6414       return;
6415
6416     if ( isLeaf() )
6417     {
6418       for ( int i = 0; i < _elements.size(); ++i )
6419         if ( !_elements[i]->IsOut( line ))
6420           foundElems.insert( _elements[i]->_element );
6421     }
6422     else
6423     {
6424       for (int i = 0; i < 8; i++)
6425         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6426     }
6427   }
6428
6429   //================================================================================
6430   /*!
6431    * \brief Return elements from leaves intersecting the sphere
6432    */
6433   //================================================================================
6434
6435   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6436                                                 const double      radius,
6437                                                 TIDSortedElemSet& foundElems)
6438   {
6439     if ( getBox()->IsOut( center, radius ))
6440       return;
6441
6442     if ( isLeaf() )
6443     {
6444       for ( int i = 0; i < _elements.size(); ++i )
6445         if ( !_elements[i]->IsOut( center, radius ))
6446           foundElems.insert( _elements[i]->_element );
6447     }
6448     else
6449     {
6450       for (int i = 0; i < 8; i++)
6451         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6452     }
6453   }
6454
6455   //================================================================================
6456   /*!
6457    * \brief Construct the element box
6458    */
6459   //================================================================================
6460
6461   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6462   {
6463     _element  = elem;
6464     _refCount = 1;
6465     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6466     while ( nIt->more() )
6467       Add( SMESH_TNodeXYZ( nIt->next() ));
6468     Enlarge( tolerance );
6469   }
6470
6471 } // namespace
6472
6473 //=======================================================================
6474 /*!
6475  * \brief Implementation of search for the elements by point and
6476  *        of classification of point in 2D mesh
6477  */
6478 //=======================================================================
6479
6480 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6481 {
6482   SMESHDS_Mesh*                _mesh;
6483   SMDS_ElemIteratorPtr         _meshPartIt;
6484   ElementBndBoxTree*           _ebbTree;
6485   SMESH_NodeSearcherImpl*      _nodeSearcher;
6486   SMDSAbs_ElementType          _elementType;
6487   double                       _tolerance;
6488   bool                         _outerFacesFound;
6489   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6490
6491   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6492     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6493   ~SMESH_ElementSearcherImpl()
6494   {
6495     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6496     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6497   }
6498   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6499                                   SMDSAbs_ElementType                type,
6500                                   vector< const SMDS_MeshElement* >& foundElements);
6501   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6502   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6503                                                  SMDSAbs_ElementType type );
6504
6505   void GetElementsNearLine( const gp_Ax1&                      line,
6506                             SMDSAbs_ElementType                type,
6507                             vector< const SMDS_MeshElement* >& foundElems);
6508   double getTolerance();
6509   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6510                             const double tolerance, double & param);
6511   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6512   bool isOuterBoundary(const SMDS_MeshElement* face) const
6513   {
6514     return _outerFaces.empty() || _outerFaces.count(face);
6515   }
6516   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6517   {
6518     const SMDS_MeshElement* _face;
6519     gp_Vec                  _faceNorm;
6520     bool                    _coincides; //!< the line lays in face plane
6521     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6522       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6523   };
6524   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6525   {
6526     SMESH_TLink      _link;
6527     TIDSortedElemSet _faces;
6528     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6529       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6530   };
6531 };
6532
6533 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6534 {
6535   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6536              << ", _coincides="<<i._coincides << ")";
6537 }
6538
6539 //=======================================================================
6540 /*!
6541  * \brief define tolerance for search
6542  */
6543 //=======================================================================
6544
6545 double SMESH_ElementSearcherImpl::getTolerance()
6546 {
6547   if ( _tolerance < 0 )
6548   {
6549     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6550
6551     _tolerance = 0;
6552     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6553     {
6554       double boxSize = _nodeSearcher->getTree()->maxSize();
6555       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6556     }
6557     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6558     {
6559       double boxSize = _ebbTree->maxSize();
6560       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6561     }
6562     if ( _tolerance == 0 )
6563     {
6564       // define tolerance by size of a most complex element
6565       int complexType = SMDSAbs_Volume;
6566       while ( complexType > SMDSAbs_All &&
6567               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6568         --complexType;
6569       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6570       double elemSize;
6571       if ( complexType == int( SMDSAbs_Node ))
6572       {
6573         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6574         elemSize = 1;
6575         if ( meshInfo.NbNodes() > 2 )
6576           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6577       }
6578       else
6579       {
6580         SMDS_ElemIteratorPtr elemIt =
6581             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6582         const SMDS_MeshElement* elem = elemIt->next();
6583         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6584         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6585         elemSize = 0;
6586         while ( nodeIt->more() )
6587         {
6588           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6589           elemSize = max( dist, elemSize );
6590         }
6591       }
6592       _tolerance = 1e-4 * elemSize;
6593     }
6594   }
6595   return _tolerance;
6596 }
6597
6598 //================================================================================
6599 /*!
6600  * \brief Find intersection of the line and an edge of face and return parameter on line
6601  */
6602 //================================================================================
6603
6604 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6605                                                      const SMDS_MeshElement* face,
6606                                                      const double            tol,
6607                                                      double &                param)
6608 {
6609   int nbInts = 0;
6610   param = 0;
6611
6612   GeomAPI_ExtremaCurveCurve anExtCC;
6613   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6614
6615   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6616   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6617   {
6618     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6619                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6620     anExtCC.Init( lineCurve, edge);
6621     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6622     {
6623       Quantity_Parameter pl, pe;
6624       anExtCC.LowerDistanceParameters( pl, pe );
6625       param += pl;
6626       if ( ++nbInts == 2 )
6627         break;
6628     }
6629   }
6630   if ( nbInts > 0 ) param /= nbInts;
6631   return nbInts > 0;
6632 }
6633 //================================================================================
6634 /*!
6635  * \brief Find all faces belonging to the outer boundary of mesh
6636  */
6637 //================================================================================
6638
6639 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6640 {
6641   if ( _outerFacesFound ) return;
6642
6643   // Collect all outer faces by passing from one outer face to another via their links
6644   // and BTW find out if there are internal faces at all.
6645
6646   // checked links and links where outer boundary meets internal one
6647   set< SMESH_TLink > visitedLinks, seamLinks;
6648
6649   // links to treat with already visited faces sharing them
6650   list < TFaceLink > startLinks;
6651
6652   // load startLinks with the first outerFace
6653   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6654   _outerFaces.insert( outerFace );
6655
6656   TIDSortedElemSet emptySet;
6657   while ( !startLinks.empty() )
6658   {
6659     const SMESH_TLink& link  = startLinks.front()._link;
6660     TIDSortedElemSet&  faces = startLinks.front()._faces;
6661
6662     outerFace = *faces.begin();
6663     // find other faces sharing the link
6664     const SMDS_MeshElement* f;
6665     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6666       faces.insert( f );
6667
6668     // select another outer face among the found
6669     const SMDS_MeshElement* outerFace2 = 0;
6670     if ( faces.size() == 2 )
6671     {
6672       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6673     }
6674     else if ( faces.size() > 2 )
6675     {
6676       seamLinks.insert( link );
6677
6678       // link direction within the outerFace
6679       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6680                    SMESH_TNodeXYZ( link.node2()));
6681       int i1 = outerFace->GetNodeIndex( link.node1() );
6682       int i2 = outerFace->GetNodeIndex( link.node2() );
6683       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6684       if ( rev ) n1n2.Reverse();
6685       // outerFace normal
6686       gp_XYZ ofNorm, fNorm;
6687       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6688       {
6689         // direction from the link inside outerFace
6690         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6691         // sort all other faces by angle with the dirInOF
6692         map< double, const SMDS_MeshElement* > angle2Face;
6693         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6694         for ( ; face != faces.end(); ++face )
6695         {
6696           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6697             continue;
6698           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6699           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6700           if ( angle < 0 ) angle += 2. * M_PI;
6701           angle2Face.insert( make_pair( angle, *face ));
6702         }
6703         if ( !angle2Face.empty() )
6704           outerFace2 = angle2Face.begin()->second;
6705       }
6706     }
6707     // store the found outer face and add its links to continue seaching from
6708     if ( outerFace2 )
6709     {
6710       _outerFaces.insert( outerFace );
6711       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6712       for ( int i = 0; i < nbNodes; ++i )
6713       {
6714         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6715         if ( visitedLinks.insert( link2 ).second )
6716           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6717       }
6718     }
6719     startLinks.pop_front();
6720   }
6721   _outerFacesFound = true;
6722
6723   if ( !seamLinks.empty() )
6724   {
6725     // There are internal boundaries touching the outher one,
6726     // find all faces of internal boundaries in order to find
6727     // faces of boundaries of holes, if any.
6728
6729   }
6730   else
6731   {
6732     _outerFaces.clear();
6733   }
6734 }
6735
6736 //=======================================================================
6737 /*!
6738  * \brief Find elements of given type where the given point is IN or ON.
6739  *        Returns nb of found elements and elements them-selves.
6740  *
6741  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6742  */
6743 //=======================================================================
6744
6745 int SMESH_ElementSearcherImpl::
6746 FindElementsByPoint(const gp_Pnt&                      point,
6747                     SMDSAbs_ElementType                type,
6748                     vector< const SMDS_MeshElement* >& foundElements)
6749 {
6750   foundElements.clear();
6751
6752   double tolerance = getTolerance();
6753
6754   // =================================================================================
6755   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6756   {
6757     if ( !_nodeSearcher )
6758       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6759
6760     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6761     if ( !closeNode ) return foundElements.size();
6762
6763     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6764       return foundElements.size(); // to far from any node
6765
6766     if ( type == SMDSAbs_Node )
6767     {
6768       foundElements.push_back( closeNode );
6769     }
6770     else
6771     {
6772       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6773       while ( elemIt->more() )
6774         foundElements.push_back( elemIt->next() );
6775     }
6776   }
6777   // =================================================================================
6778   else // elements more complex than 0D
6779   {
6780     if ( !_ebbTree || _elementType != type )
6781     {
6782       if ( _ebbTree ) delete _ebbTree;
6783       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6784     }
6785     TIDSortedElemSet suspectElems;
6786     _ebbTree->getElementsNearPoint( point, suspectElems );
6787     TIDSortedElemSet::iterator elem = suspectElems.begin();
6788     for ( ; elem != suspectElems.end(); ++elem )
6789       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6790         foundElements.push_back( *elem );
6791   }
6792   return foundElements.size();
6793 }
6794
6795 //=======================================================================
6796 /*!
6797  * \brief Find an element of given type most close to the given point
6798  *
6799  * WARNING: Only face search is implemeneted so far
6800  */
6801 //=======================================================================
6802
6803 const SMDS_MeshElement*
6804 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6805                                           SMDSAbs_ElementType type )
6806 {
6807   const SMDS_MeshElement* closestElem = 0;
6808
6809   if ( type == SMDSAbs_Face )
6810   {
6811     if ( !_ebbTree || _elementType != type )
6812     {
6813       if ( _ebbTree ) delete _ebbTree;
6814       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6815     }
6816     TIDSortedElemSet suspectElems;
6817     _ebbTree->getElementsNearPoint( point, suspectElems );
6818
6819     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6820     {
6821       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6822                                  _ebbTree->getBox()->CornerMax() );
6823       double radius;
6824       if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6825         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6826       else
6827         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6828       while ( suspectElems.empty() )
6829       {
6830         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6831         radius *= 1.1;
6832       }
6833     }
6834     double minDist = std::numeric_limits<double>::max();
6835     multimap< double, const SMDS_MeshElement* > dist2face;
6836     TIDSortedElemSet::iterator elem = suspectElems.begin();
6837     for ( ; elem != suspectElems.end(); ++elem )
6838     {
6839       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6840                                                    point );
6841       if ( dist < minDist + 1e-10)
6842       {
6843         minDist = dist;
6844         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6845       }
6846     }
6847     if ( !dist2face.empty() )
6848     {
6849       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6850       closestElem = d2f->second;
6851       // if there are several elements at the same distance, select one
6852       // with GC closest to the point
6853       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6854       double minDistToGC = 0;
6855       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6856       {
6857         if ( minDistToGC == 0 )
6858         {
6859           gp_XYZ gc(0,0,0);
6860           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6861                            TXyzIterator(), gc ) / closestElem->NbNodes();
6862           minDistToGC = point.SquareDistance( gc );
6863         }
6864         gp_XYZ gc(0,0,0);
6865         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6866                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6867         double d = point.SquareDistance( gc );
6868         if ( d < minDistToGC )
6869         {
6870           minDistToGC = d;
6871           closestElem = d2f->second;
6872         }
6873       }
6874       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6875       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6876     }
6877   }
6878   else
6879   {
6880     // NOT IMPLEMENTED SO FAR
6881   }
6882   return closestElem;
6883 }
6884
6885
6886 //================================================================================
6887 /*!
6888  * \brief Classify the given point in the closed 2D mesh
6889  */
6890 //================================================================================
6891
6892 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6893 {
6894   double tolerance = getTolerance();
6895   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6896   {
6897     if ( _ebbTree ) delete _ebbTree;
6898     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6899   }
6900   // Algo: analyse transition of a line starting at the point through mesh boundary;
6901   // try three lines parallel to axis of the coordinate system and perform rough
6902   // analysis. If solution is not clear perform thorough analysis.
6903
6904   const int nbAxes = 3;
6905   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6906   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6907   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6908   multimap< int, int > nbInt2Axis; // to find the simplest case
6909   for ( int axis = 0; axis < nbAxes; ++axis )
6910   {
6911     gp_Ax1 lineAxis( point, axisDir[axis]);
6912     gp_Lin line    ( lineAxis );
6913
6914     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6915     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6916
6917     // Intersect faces with the line
6918
6919     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6920     TIDSortedElemSet::iterator face = suspectFaces.begin();
6921     for ( ; face != suspectFaces.end(); ++face )
6922     {
6923       // get face plane
6924       gp_XYZ fNorm;
6925       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6926       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6927
6928       // perform intersection
6929       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6930       if ( !intersection.IsDone() )
6931         continue;
6932       if ( intersection.IsInQuadric() )
6933       {
6934         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6935       }
6936       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6937       {
6938         gp_Pnt intersectionPoint = intersection.Point(1);
6939         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6940           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6941       }
6942     }
6943     // Analyse intersections roughly
6944
6945     int nbInter = u2inters.size();
6946     if ( nbInter == 0 )
6947       return TopAbs_OUT;
6948
6949     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6950     if ( nbInter == 1 ) // not closed mesh
6951       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6952
6953     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6954       return TopAbs_ON;
6955
6956     if ( (f<0) == (l<0) )
6957       return TopAbs_OUT;
6958
6959     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6960     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
6961     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6962       return TopAbs_IN;
6963
6964     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6965
6966     if ( _outerFacesFound ) break; // pass to thorough analysis
6967
6968   } // three attempts - loop on CS axes
6969
6970   // Analyse intersections thoroughly.
6971   // We make two loops maximum, on the first one we only exclude touching intersections,
6972   // on the second, if situation is still unclear, we gather and use information on
6973   // position of faces (internal or outer). If faces position is already gathered,
6974   // we make the second loop right away.
6975
6976   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6977   {
6978     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6979     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6980     {
6981       int axis = nb_axis->second;
6982       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6983
6984       gp_Ax1 lineAxis( point, axisDir[axis]);
6985       gp_Lin line    ( lineAxis );
6986
6987       // add tangent intersections to u2inters
6988       double param;
6989       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6990       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6991         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6992           u2inters.insert(make_pair( param, *tgtInt ));
6993       tangentInters[ axis ].clear();
6994
6995       // Count intersections before and after the point excluding touching ones.
6996       // If hasPositionInfo we count intersections of outer boundary only
6997
6998       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6999       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7000       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7001       bool ok = ! u_int1->second._coincides;
7002       while ( ok && u_int1 != u2inters.end() )
7003       {
7004         double u = u_int1->first;
7005         bool touchingInt = false;
7006         if ( ++u_int2 != u2inters.end() )
7007         {
7008           // skip intersections at the same point (if the line passes through edge or node)
7009           int nbSamePnt = 0;
7010           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7011           {
7012             ++nbSamePnt;
7013             ++u_int2;
7014           }
7015
7016           // skip tangent intersections
7017           int nbTgt = 0;
7018           const SMDS_MeshElement* prevFace = u_int1->second._face;
7019           while ( ok && u_int2->second._coincides )
7020           {
7021             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7022               ok = false;
7023             else
7024             {
7025               nbTgt++;
7026               u_int2++;
7027               ok = ( u_int2 != u2inters.end() );
7028             }
7029           }
7030           if ( !ok ) break;
7031
7032           // skip intersections at the same point after tangent intersections
7033           if ( nbTgt > 0 )
7034           {
7035             double u2 = u_int2->first;
7036             ++u_int2;
7037             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7038             {
7039               ++nbSamePnt;
7040               ++u_int2;
7041             }
7042           }
7043           // decide if we skipped a touching intersection
7044           if ( nbSamePnt + nbTgt > 0 )
7045           {
7046             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7047             map< double, TInters >::iterator u_int = u_int1;
7048             for ( ; u_int != u_int2; ++u_int )
7049             {
7050               if ( u_int->second._coincides ) continue;
7051               double dot = u_int->second._faceNorm * line.Direction();
7052               if ( dot > maxDot ) maxDot = dot;
7053               if ( dot < minDot ) minDot = dot;
7054             }
7055             touchingInt = ( minDot*maxDot < 0 );
7056           }
7057         }
7058         if ( !touchingInt )
7059         {
7060           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7061           {
7062             if ( u < 0 )
7063               ++nbIntBeforePoint;
7064             else
7065               ++nbIntAfterPoint;
7066           }
7067           if ( u < f ) f = u;
7068           if ( u > l ) l = u;
7069         }
7070
7071         u_int1 = u_int2; // to next intersection
7072
7073       } // loop on intersections with one line
7074
7075       if ( ok )
7076       {
7077         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7078           return TopAbs_ON;
7079
7080         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
7081           return TopAbs_OUT;
7082
7083         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7084           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7085
7086         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7087           return TopAbs_IN;
7088
7089         if ( (f<0) == (l<0) )
7090           return TopAbs_OUT;
7091
7092         if ( hasPositionInfo )
7093           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7094       }
7095     } // loop on intersections of the tree lines - thorough analysis
7096
7097     if ( !hasPositionInfo )
7098     {
7099       // gather info on faces position - is face in the outer boundary or not
7100       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7101       findOuterBoundary( u2inters.begin()->second._face );
7102     }
7103
7104   } // two attempts - with and w/o faces position info in the mesh
7105
7106   return TopAbs_UNKNOWN;
7107 }
7108
7109 //=======================================================================
7110 /*!
7111  * \brief Return elements possibly intersecting the line
7112  */
7113 //=======================================================================
7114
7115 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7116                                                      SMDSAbs_ElementType                type,
7117                                                      vector< const SMDS_MeshElement* >& foundElems)
7118 {
7119   if ( !_ebbTree || _elementType != type )
7120   {
7121     if ( _ebbTree ) delete _ebbTree;
7122     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7123   }
7124   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7125   _ebbTree->getElementsNearLine( line, suspectFaces );
7126   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7127 }
7128
7129 //=======================================================================
7130 /*!
7131  * \brief Return SMESH_ElementSearcher
7132  */
7133 //=======================================================================
7134
7135 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7136 {
7137   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7138 }
7139
7140 //=======================================================================
7141 /*!
7142  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7143  */
7144 //=======================================================================
7145
7146 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7147 {
7148   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7149 }
7150
7151 //=======================================================================
7152 /*!
7153  * \brief Return true if the point is IN or ON of the element
7154  */
7155 //=======================================================================
7156
7157 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7158 {
7159   if ( element->GetType() == SMDSAbs_Volume)
7160   {
7161     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7162   }
7163
7164   // get ordered nodes
7165
7166   vector< gp_XYZ > xyz;
7167   vector<const SMDS_MeshNode*> nodeList;
7168
7169   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7170   if ( element->IsQuadratic() ) {
7171     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7172       nodeIt = f->interlacedNodesElemIterator();
7173     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7174       nodeIt = e->interlacedNodesElemIterator();
7175   }
7176   while ( nodeIt->more() )
7177     {
7178       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7179       xyz.push_back( SMESH_TNodeXYZ(node) );
7180       nodeList.push_back(node);
7181     }
7182
7183   int i, nbNodes = element->NbNodes();
7184
7185   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7186   {
7187     // compute face normal
7188     gp_Vec faceNorm(0,0,0);
7189     xyz.push_back( xyz.front() );
7190     nodeList.push_back( nodeList.front() );
7191     for ( i = 0; i < nbNodes; ++i )
7192     {
7193       gp_Vec edge1( xyz[i+1], xyz[i]);
7194       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7195       faceNorm += edge1 ^ edge2;
7196     }
7197     double normSize = faceNorm.Magnitude();
7198     if ( normSize <= tol )
7199     {
7200       // degenerated face: point is out if it is out of all face edges
7201       for ( i = 0; i < nbNodes; ++i )
7202       {
7203         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7204         if ( !IsOut( &edge, point, tol ))
7205           return false;
7206       }
7207       return true;
7208     }
7209     faceNorm /= normSize;
7210
7211     // check if the point lays on face plane
7212     gp_Vec n2p( xyz[0], point );
7213     if ( fabs( n2p * faceNorm ) > tol )
7214       return true; // not on face plane
7215
7216     // check if point is out of face boundary:
7217     // define it by closest transition of a ray point->infinity through face boundary
7218     // on the face plane.
7219     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7220     // to find intersections of the ray with the boundary.
7221     gp_Vec ray = n2p;
7222     gp_Vec plnNorm = ray ^ faceNorm;
7223     normSize = plnNorm.Magnitude();
7224     if ( normSize <= tol ) return false; // point coincides with the first node
7225     plnNorm /= normSize;
7226     // for each node of the face, compute its signed distance to the plane
7227     vector<double> dist( nbNodes + 1);
7228     for ( i = 0; i < nbNodes; ++i )
7229     {
7230       gp_Vec n2p( xyz[i], point );
7231       dist[i] = n2p * plnNorm;
7232     }
7233     dist.back() = dist.front();
7234     // find the closest intersection
7235     int    iClosest = -1;
7236     double rClosest, distClosest = 1e100;;
7237     gp_Pnt pClosest;
7238     for ( i = 0; i < nbNodes; ++i )
7239     {
7240       double r;
7241       if ( fabs( dist[i]) < tol )
7242         r = 0.;
7243       else if ( fabs( dist[i+1]) < tol )
7244         r = 1.;
7245       else if ( dist[i] * dist[i+1] < 0 )
7246         r = dist[i] / ( dist[i] - dist[i+1] );
7247       else
7248         continue; // no intersection
7249       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7250       gp_Vec p2int ( point, pInt);
7251       if ( p2int * ray > -tol ) // right half-space
7252       {
7253         double intDist = p2int.SquareMagnitude();
7254         if ( intDist < distClosest )
7255         {
7256           iClosest = i;
7257           rClosest = r;
7258           pClosest = pInt;
7259           distClosest = intDist;
7260         }
7261       }
7262     }
7263     if ( iClosest < 0 )
7264       return true; // no intesections - out
7265
7266     // analyse transition
7267     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7268     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7269     gp_Vec p2int ( point, pClosest );
7270     bool out = (edgeNorm * p2int) < -tol;
7271     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7272       return out;
7273
7274     // ray pass through a face node; analyze transition through an adjacent edge
7275     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7276     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7277     gp_Vec edgeAdjacent( p1, p2 );
7278     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7279     bool out2 = (edgeNorm2 * p2int) < -tol;
7280
7281     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7282     return covexCorner ? (out || out2) : (out && out2);
7283   }
7284   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7285   {
7286     // point is out of edge if it is NOT ON any straight part of edge
7287     // (we consider quadratic edge as being composed of two straight parts)
7288     for ( i = 1; i < nbNodes; ++i )
7289     {
7290       gp_Vec edge( xyz[i-1], xyz[i]);
7291       gp_Vec n1p ( xyz[i-1], point);
7292       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7293       if ( dist > tol )
7294         continue;
7295       gp_Vec n2p( xyz[i], point );
7296       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7297         continue;
7298       return false; // point is ON this part
7299     }
7300     return true;
7301   }
7302   // Node or 0D element -------------------------------------------------------------------------
7303   {
7304     gp_Vec n2p ( xyz[0], point );
7305     return n2p.Magnitude() <= tol;
7306   }
7307   return true;
7308 }
7309
7310 //=======================================================================
7311
7312 namespace
7313 {
7314   // Position of a point relative to a segment
7315   //            .           .
7316   //            .  LEFT     .
7317   //            .           .
7318   //  VERTEX 1  o----ON----->  VERTEX 2
7319   //            .           .
7320   //            .  RIGHT    .
7321   //            .           .
7322   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7323                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7324   struct PointPos
7325   {
7326     PositionName _name;
7327     int          _index; // index of vertex or segment
7328
7329     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7330     bool operator < (const PointPos& other ) const
7331     {
7332       if ( _name == other._name )
7333         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7334       return _name < other._name;
7335     }
7336   };
7337
7338   //================================================================================
7339   /*!
7340    * \brief Return of a point relative to a segment
7341    *  \param point2D      - the point to analyze position of
7342    *  \param xyVec        - end points of segments
7343    *  \param index0       - 0-based index of the first point of segment
7344    *  \param posToFindOut - flags of positions to detect
7345    *  \retval PointPos - point position
7346    */
7347   //================================================================================
7348
7349   PointPos getPointPosition( const gp_XY& point2D,
7350                              const gp_XY* segEnds,
7351                              const int    index0 = 0,
7352                              const int    posToFindOut = POS_ALL)
7353   {
7354     const gp_XY& p1 = segEnds[ index0   ];
7355     const gp_XY& p2 = segEnds[ index0+1 ];
7356     const gp_XY grad = p2 - p1;
7357
7358     if ( posToFindOut & POS_VERTEX )
7359     {
7360       // check if the point2D is at "vertex 1" zone
7361       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7362                                   p1.Y() + grad.X() ) };
7363       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7364         return PointPos( POS_VERTEX, index0 );
7365
7366       // check if the point2D is at "vertex 2" zone
7367       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7368                                   p2.Y() + grad.X() ) };
7369       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7370         return PointPos( POS_VERTEX, index0 + 1);
7371     }
7372     double edgeEquation =
7373       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7374     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7375   }
7376 }
7377
7378 //=======================================================================
7379 /*!
7380  * \brief Return minimal distance from a point to a face
7381  *
7382  * Currently we ignore non-planarity and 2nd order of face
7383  */
7384 //=======================================================================
7385
7386 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7387                                       const gp_Pnt&        point )
7388 {
7389   double badDistance = -1;
7390   if ( !face ) return badDistance;
7391
7392   // coordinates of nodes (medium nodes, if any, ignored)
7393   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7394   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7395   xyz.resize( face->NbCornerNodes()+1 );
7396
7397   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7398   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7399   gp_Trsf trsf;
7400   gp_Vec OZ ( xyz[0], xyz[1] );
7401   gp_Vec OX ( xyz[0], xyz[2] );
7402   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7403   {
7404     if ( xyz.size() < 4 ) return badDistance;
7405     OZ = gp_Vec ( xyz[0], xyz[2] );
7406     OX = gp_Vec ( xyz[0], xyz[3] );
7407   }
7408   gp_Ax3 tgtCS;
7409   try {
7410     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7411   }
7412   catch ( Standard_Failure ) {
7413     return badDistance;
7414   }
7415   trsf.SetTransformation( tgtCS );
7416
7417   // move all the nodes to 2D
7418   vector<gp_XY> xy( xyz.size() );
7419   for ( size_t i = 0;i < xyz.size()-1; ++i )
7420   {
7421     gp_XYZ p3d = xyz[i];
7422     trsf.Transforms( p3d );
7423     xy[i].SetCoord( p3d.X(), p3d.Z() );
7424   }
7425   xyz.back() = xyz.front();
7426   xy.back() = xy.front();
7427
7428   // // move the point in 2D
7429   gp_XYZ tmpPnt = point.XYZ();
7430   trsf.Transforms( tmpPnt );
7431   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7432
7433   // loop on segments of the face to analyze point position ralative to the face
7434   set< PointPos > pntPosSet;
7435   for ( size_t i = 1; i < xy.size(); ++i )
7436   {
7437     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7438     pntPosSet.insert( pos );
7439   }
7440
7441   // compute distance
7442   PointPos pos = *pntPosSet.begin();
7443   // cout << "Face " << face->GetID() << " DIST: ";
7444   switch ( pos._name )
7445   {
7446   case POS_LEFT: {
7447     // point is most close to a segment
7448     gp_Vec p0p1( point, xyz[ pos._index ] );
7449     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7450     p1p2.Normalize();
7451     double projDist = p0p1 * p1p2; // distance projected to the segment
7452     gp_Vec projVec = p1p2 * projDist;
7453     gp_Vec distVec = p0p1 - projVec;
7454     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7455     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7456     return distVec.Magnitude();
7457   }
7458   case POS_RIGHT: {
7459     // point is inside the face
7460     double distToFacePlane = tmpPnt.Y();
7461     // cout << distToFacePlane << ", INSIDE " << endl;
7462     return Abs( distToFacePlane );
7463   }
7464   case POS_VERTEX: {
7465     // point is most close to a node
7466     gp_Vec distVec( point, xyz[ pos._index ]);
7467     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7468     return distVec.Magnitude();
7469   }
7470   }
7471   return badDistance;
7472 }
7473
7474 //=======================================================================
7475 //function : SimplifyFace
7476 //purpose  :
7477 //=======================================================================
7478 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7479                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7480                                     vector<int>&                        quantities) const
7481 {
7482   int nbNodes = faceNodes.size();
7483
7484   if (nbNodes < 3)
7485     return 0;
7486
7487   set<const SMDS_MeshNode*> nodeSet;
7488
7489   // get simple seq of nodes
7490   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7491   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7492   int iSimple = 0, nbUnique = 0;
7493
7494   simpleNodes[iSimple++] = faceNodes[0];
7495   nbUnique++;
7496   for (int iCur = 1; iCur < nbNodes; iCur++) {
7497     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7498       simpleNodes[iSimple++] = faceNodes[iCur];
7499       if (nodeSet.insert( faceNodes[iCur] ).second)
7500         nbUnique++;
7501     }
7502   }
7503   int nbSimple = iSimple;
7504   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7505     nbSimple--;
7506     iSimple--;
7507   }
7508
7509   if (nbUnique < 3)
7510     return 0;
7511
7512   // separate loops
7513   int nbNew = 0;
7514   bool foundLoop = (nbSimple > nbUnique);
7515   while (foundLoop) {
7516     foundLoop = false;
7517     set<const SMDS_MeshNode*> loopSet;
7518     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7519       const SMDS_MeshNode* n = simpleNodes[iSimple];
7520       if (!loopSet.insert( n ).second) {
7521         foundLoop = true;
7522
7523         // separate loop
7524         int iC = 0, curLast = iSimple;
7525         for (; iC < curLast; iC++) {
7526           if (simpleNodes[iC] == n) break;
7527         }
7528         int loopLen = curLast - iC;
7529         if (loopLen > 2) {
7530           // create sub-element
7531           nbNew++;
7532           quantities.push_back(loopLen);
7533           for (; iC < curLast; iC++) {
7534             poly_nodes.push_back(simpleNodes[iC]);
7535           }
7536         }
7537         // shift the rest nodes (place from the first loop position)
7538         for (iC = curLast + 1; iC < nbSimple; iC++) {
7539           simpleNodes[iC - loopLen] = simpleNodes[iC];
7540         }
7541         nbSimple -= loopLen;
7542         iSimple -= loopLen;
7543       }
7544     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7545   } // while (foundLoop)
7546
7547   if (iSimple > 2) {
7548     nbNew++;
7549     quantities.push_back(iSimple);
7550     for (int i = 0; i < iSimple; i++)
7551       poly_nodes.push_back(simpleNodes[i]);
7552   }
7553
7554   return nbNew;
7555 }
7556
7557 //=======================================================================
7558 //function : MergeNodes
7559 //purpose  : In each group, the cdr of nodes are substituted by the first one
7560 //           in all elements.
7561 //=======================================================================
7562
7563 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7564 {
7565   MESSAGE("MergeNodes");
7566   myLastCreatedElems.Clear();
7567   myLastCreatedNodes.Clear();
7568
7569   SMESHDS_Mesh* aMesh = GetMeshDS();
7570
7571   TNodeNodeMap nodeNodeMap; // node to replace - new node
7572   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7573   list< int > rmElemIds, rmNodeIds;
7574
7575   // Fill nodeNodeMap and elems
7576
7577   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7578   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7579     list<const SMDS_MeshNode*>& nodes = *grIt;
7580     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7581     const SMDS_MeshNode* nToKeep = *nIt;
7582     //MESSAGE("node to keep " << nToKeep->GetID());
7583     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7584       const SMDS_MeshNode* nToRemove = *nIt;
7585       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7586       if ( nToRemove != nToKeep ) {
7587         //MESSAGE("  node to remove " << nToRemove->GetID());
7588         rmNodeIds.push_back( nToRemove->GetID() );
7589         AddToSameGroups( nToKeep, nToRemove, aMesh );
7590       }
7591
7592       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7593       while ( invElemIt->more() ) {
7594         const SMDS_MeshElement* elem = invElemIt->next();
7595         elems.insert(elem);
7596       }
7597     }
7598   }
7599   // Change element nodes or remove an element
7600
7601   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7602   for ( ; eIt != elems.end(); eIt++ ) {
7603     const SMDS_MeshElement* elem = *eIt;
7604     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7605     int nbNodes = elem->NbNodes();
7606     int aShapeId = FindShape( elem );
7607
7608     set<const SMDS_MeshNode*> nodeSet;
7609     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7610     int iUnique = 0, iCur = 0, nbRepl = 0;
7611     vector<int> iRepl( nbNodes );
7612
7613     // get new seq of nodes
7614     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7615     while ( itN->more() ) {
7616       const SMDS_MeshNode* n =
7617         static_cast<const SMDS_MeshNode*>( itN->next() );
7618
7619       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7620       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7621         n = (*nnIt).second;
7622         // BUG 0020185: begin
7623         {
7624           bool stopRecur = false;
7625           set<const SMDS_MeshNode*> nodesRecur;
7626           nodesRecur.insert(n);
7627           while (!stopRecur) {
7628             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7629             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7630               n = (*nnIt_i).second;
7631               if (!nodesRecur.insert(n).second) {
7632                 // error: recursive dependancy
7633                 stopRecur = true;
7634               }
7635             }
7636             else
7637               stopRecur = true;
7638           }
7639         }
7640         // BUG 0020185: end
7641       }
7642       curNodes[ iCur ] = n;
7643       bool isUnique = nodeSet.insert( n ).second;
7644       if ( isUnique )
7645         uniqueNodes[ iUnique++ ] = n;
7646       else
7647         iRepl[ nbRepl++ ] = iCur;
7648       iCur++;
7649     }
7650
7651     // Analyse element topology after replacement
7652
7653     bool isOk = true;
7654     int nbUniqueNodes = nodeSet.size();
7655     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7656     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7657       // Polygons and Polyhedral volumes
7658       if (elem->IsPoly()) {
7659
7660         if (elem->GetType() == SMDSAbs_Face) {
7661           // Polygon
7662           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7663           int inode = 0;
7664           for (; inode < nbNodes; inode++) {
7665             face_nodes[inode] = curNodes[inode];
7666           }
7667
7668           vector<const SMDS_MeshNode *> polygons_nodes;
7669           vector<int> quantities;
7670           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7671           if (nbNew > 0) {
7672             inode = 0;
7673             for (int iface = 0; iface < nbNew; iface++) {
7674               int nbNodes = quantities[iface];
7675               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7676               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7677                 poly_nodes[ii] = polygons_nodes[inode];
7678               }
7679               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7680               myLastCreatedElems.Append(newElem);
7681               if (aShapeId)
7682                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7683             }
7684
7685             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7686             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7687             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7688             int quid =0;
7689             if (nbNew > 0) quid = nbNew - 1;
7690             vector<int> newquant(quantities.begin()+quid, quantities.end());
7691             const SMDS_MeshElement* newElem = 0;
7692             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7693             myLastCreatedElems.Append(newElem);
7694             if ( aShapeId && newElem )
7695               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7696             rmElemIds.push_back(elem->GetID());
7697           }
7698           else {
7699             rmElemIds.push_back(elem->GetID());
7700           }
7701
7702         }
7703         else if (elem->GetType() == SMDSAbs_Volume) {
7704           // Polyhedral volume
7705           if (nbUniqueNodes < 4) {
7706             rmElemIds.push_back(elem->GetID());
7707           }
7708           else {
7709             // each face has to be analyzed in order to check volume validity
7710             const SMDS_VtkVolume* aPolyedre =
7711               dynamic_cast<const SMDS_VtkVolume*>( elem );
7712             if (aPolyedre) {
7713               int nbFaces = aPolyedre->NbFaces();
7714
7715               vector<const SMDS_MeshNode *> poly_nodes;
7716               vector<int> quantities;
7717
7718               for (int iface = 1; iface <= nbFaces; iface++) {
7719                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7720                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7721
7722                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7723                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7724                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7725                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7726                     faceNode = (*nnIt).second;
7727                   }
7728                   faceNodes[inode - 1] = faceNode;
7729                 }
7730
7731                 SimplifyFace(faceNodes, poly_nodes, quantities);
7732               }
7733
7734               if (quantities.size() > 3) {
7735                 // to be done: remove coincident faces
7736               }
7737
7738               if (quantities.size() > 3)
7739                 {
7740                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7741                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7742                   const SMDS_MeshElement* newElem = 0;
7743                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7744                   myLastCreatedElems.Append(newElem);
7745                   if ( aShapeId && newElem )
7746                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7747                   rmElemIds.push_back(elem->GetID());
7748                 }
7749             }
7750             else {
7751               rmElemIds.push_back(elem->GetID());
7752             }
7753           }
7754         }
7755         else {
7756         }
7757
7758         continue;
7759       } // poly element
7760
7761       // Regular elements
7762       // TODO not all the possible cases are solved. Find something more generic?
7763       switch ( nbNodes ) {
7764       case 2: ///////////////////////////////////// EDGE
7765         isOk = false; break;
7766       case 3: ///////////////////////////////////// TRIANGLE
7767         isOk = false; break;
7768       case 4:
7769         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7770           isOk = false;
7771         else { //////////////////////////////////// QUADRANGLE
7772           if ( nbUniqueNodes < 3 )
7773             isOk = false;
7774           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7775             isOk = false; // opposite nodes stick
7776           //MESSAGE("isOk " << isOk);
7777         }
7778         break;
7779       case 6: ///////////////////////////////////// PENTAHEDRON
7780         if ( nbUniqueNodes == 4 ) {
7781           // ---------------------------------> tetrahedron
7782           if (nbRepl == 3 &&
7783               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7784             // all top nodes stick: reverse a bottom
7785             uniqueNodes[ 0 ] = curNodes [ 1 ];
7786             uniqueNodes[ 1 ] = curNodes [ 0 ];
7787           }
7788           else if (nbRepl == 3 &&
7789                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7790             // all bottom nodes stick: set a top before
7791             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7792             uniqueNodes[ 0 ] = curNodes [ 3 ];
7793             uniqueNodes[ 1 ] = curNodes [ 4 ];
7794             uniqueNodes[ 2 ] = curNodes [ 5 ];
7795           }
7796           else if (nbRepl == 4 &&
7797                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7798             // a lateral face turns into a line: reverse a bottom
7799             uniqueNodes[ 0 ] = curNodes [ 1 ];
7800             uniqueNodes[ 1 ] = curNodes [ 0 ];
7801           }
7802           else
7803             isOk = false;
7804         }
7805         else if ( nbUniqueNodes == 5 ) {
7806           // PENTAHEDRON --------------------> 2 tetrahedrons
7807           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7808             // a bottom node sticks with a linked top one
7809             // 1.
7810             SMDS_MeshElement* newElem =
7811               aMesh->AddVolume(curNodes[ 3 ],
7812                                curNodes[ 4 ],
7813                                curNodes[ 5 ],
7814                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7815             myLastCreatedElems.Append(newElem);
7816             if ( aShapeId )
7817               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7818             // 2. : reverse a bottom
7819             uniqueNodes[ 0 ] = curNodes [ 1 ];
7820             uniqueNodes[ 1 ] = curNodes [ 0 ];
7821             nbUniqueNodes = 4;
7822           }
7823           else
7824             isOk = false;
7825         }
7826         else
7827           isOk = false;
7828         break;
7829       case 8: {
7830         if(elem->IsQuadratic()) { // Quadratic quadrangle
7831           //   1    5    2
7832           //    +---+---+
7833           //    |       |
7834           //    |       |
7835           //   4+       +6
7836           //    |       |
7837           //    |       |
7838           //    +---+---+
7839           //   0    7    3
7840           isOk = false;
7841           if(nbRepl==2) {
7842             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7843           }
7844           if(nbRepl==3) {
7845             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7846             nbUniqueNodes = 6;
7847             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7848               uniqueNodes[0] = curNodes[0];
7849               uniqueNodes[1] = curNodes[2];
7850               uniqueNodes[2] = curNodes[3];
7851               uniqueNodes[3] = curNodes[5];
7852               uniqueNodes[4] = curNodes[6];
7853               uniqueNodes[5] = curNodes[7];
7854               isOk = true;
7855             }
7856             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7857               uniqueNodes[0] = curNodes[0];
7858               uniqueNodes[1] = curNodes[1];
7859               uniqueNodes[2] = curNodes[2];
7860               uniqueNodes[3] = curNodes[4];
7861               uniqueNodes[4] = curNodes[5];
7862               uniqueNodes[5] = curNodes[6];
7863               isOk = true;
7864             }
7865             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7866               uniqueNodes[0] = curNodes[1];
7867               uniqueNodes[1] = curNodes[2];
7868               uniqueNodes[2] = curNodes[3];
7869               uniqueNodes[3] = curNodes[5];
7870               uniqueNodes[4] = curNodes[6];
7871               uniqueNodes[5] = curNodes[0];
7872               isOk = true;
7873             }
7874             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7875               uniqueNodes[0] = curNodes[0];
7876               uniqueNodes[1] = curNodes[1];
7877               uniqueNodes[2] = curNodes[3];
7878               uniqueNodes[3] = curNodes[4];
7879               uniqueNodes[4] = curNodes[6];
7880               uniqueNodes[5] = curNodes[7];
7881               isOk = true;
7882             }
7883             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7884               uniqueNodes[0] = curNodes[0];
7885               uniqueNodes[1] = curNodes[2];
7886               uniqueNodes[2] = curNodes[3];
7887               uniqueNodes[3] = curNodes[1];
7888               uniqueNodes[4] = curNodes[6];
7889               uniqueNodes[5] = curNodes[7];
7890               isOk = true;
7891             }
7892             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7893               uniqueNodes[0] = curNodes[0];
7894               uniqueNodes[1] = curNodes[1];
7895               uniqueNodes[2] = curNodes[2];
7896               uniqueNodes[3] = curNodes[4];
7897               uniqueNodes[4] = curNodes[5];
7898               uniqueNodes[5] = curNodes[7];
7899               isOk = true;
7900             }
7901             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7902               uniqueNodes[0] = curNodes[0];
7903               uniqueNodes[1] = curNodes[1];
7904               uniqueNodes[2] = curNodes[3];
7905               uniqueNodes[3] = curNodes[4];
7906               uniqueNodes[4] = curNodes[2];
7907               uniqueNodes[5] = curNodes[7];
7908               isOk = true;
7909             }
7910             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7911               uniqueNodes[0] = curNodes[0];
7912               uniqueNodes[1] = curNodes[1];
7913               uniqueNodes[2] = curNodes[2];
7914               uniqueNodes[3] = curNodes[4];
7915               uniqueNodes[4] = curNodes[5];
7916               uniqueNodes[5] = curNodes[3];
7917               isOk = true;
7918             }
7919           }
7920           if(nbRepl==4) {
7921             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7922           }
7923           if(nbRepl==5) {
7924             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7925           }
7926           break;
7927         }
7928         //////////////////////////////////// HEXAHEDRON
7929         isOk = false;
7930         SMDS_VolumeTool hexa (elem);
7931         hexa.SetExternalNormal();
7932         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7933           //////////////////////// HEX ---> 1 tetrahedron
7934           for ( int iFace = 0; iFace < 6; iFace++ ) {
7935             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7936             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7937                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7938                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7939               // one face turns into a point ...
7940               int iOppFace = hexa.GetOppFaceIndex( iFace );
7941               ind = hexa.GetFaceNodesIndices( iOppFace );
7942               int nbStick = 0;
7943               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7944                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7945                   nbStick++;
7946               }
7947               if ( nbStick == 1 ) {
7948                 // ... and the opposite one - into a triangle.
7949                 // set a top node
7950                 ind = hexa.GetFaceNodesIndices( iFace );
7951                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7952                 isOk = true;
7953               }
7954               break;
7955             }
7956           }
7957         }
7958         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7959           //////////////////////// HEX ---> 1 prism
7960           int nbTria = 0, iTria[3];
7961           const int *ind; // indices of face nodes
7962           // look for triangular faces
7963           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7964             ind = hexa.GetFaceNodesIndices( iFace );
7965             TIDSortedNodeSet faceNodes;
7966             for ( iCur = 0; iCur < 4; iCur++ )
7967               faceNodes.insert( curNodes[ind[iCur]] );
7968             if ( faceNodes.size() == 3 )
7969               iTria[ nbTria++ ] = iFace;
7970           }
7971           // check if triangles are opposite
7972           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7973           {
7974             isOk = true;
7975             // set nodes of the bottom triangle
7976             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7977             vector<int> indB;
7978             for ( iCur = 0; iCur < 4; iCur++ )
7979               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7980                 indB.push_back( ind[iCur] );
7981             if ( !hexa.IsForward() )
7982               std::swap( indB[0], indB[2] );
7983             for ( iCur = 0; iCur < 3; iCur++ )
7984               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7985             // set nodes of the top triangle
7986             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7987             for ( iCur = 0; iCur < 3; ++iCur )
7988               for ( int j = 0; j < 4; ++j )
7989                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7990                 {
7991                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7992                   break;
7993                 }
7994           }
7995           break;
7996         }
7997         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7998           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7999           for ( int iFace = 0; iFace < 6; iFace++ ) {
8000             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8001             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8002                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8003                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8004               // one face turns into a point ...
8005               int iOppFace = hexa.GetOppFaceIndex( iFace );
8006               ind = hexa.GetFaceNodesIndices( iOppFace );
8007               int nbStick = 0;
8008               iUnique = 2;  // reverse a tetrahedron 1 bottom
8009               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8010                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8011                   nbStick++;
8012                 else if ( iUnique >= 0 )
8013                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8014               }
8015               if ( nbStick == 0 ) {
8016                 // ... and the opposite one is a quadrangle
8017                 // set a top node
8018                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8019                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8020                 nbUniqueNodes = 4;
8021                 // tetrahedron 2
8022                 SMDS_MeshElement* newElem =
8023                   aMesh->AddVolume(curNodes[ind[ 0 ]],
8024                                    curNodes[ind[ 3 ]],
8025                                    curNodes[ind[ 2 ]],
8026                                    curNodes[indTop[ 0 ]]);
8027                 myLastCreatedElems.Append(newElem);
8028                 if ( aShapeId )
8029                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8030                 isOk = true;
8031               }
8032               break;
8033             }
8034           }
8035         }
8036         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8037           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8038           // find indices of quad and tri faces
8039           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8040           for ( iFace = 0; iFace < 6; iFace++ ) {
8041             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8042             nodeSet.clear();
8043             for ( iCur = 0; iCur < 4; iCur++ )
8044               nodeSet.insert( curNodes[ind[ iCur ]] );
8045             nbUniqueNodes = nodeSet.size();
8046             if ( nbUniqueNodes == 3 )
8047               iTriFace[ nbTri++ ] = iFace;
8048             else if ( nbUniqueNodes == 4 )
8049               iQuadFace[ nbQuad++ ] = iFace;
8050           }
8051           if (nbQuad == 2 && nbTri == 4 &&
8052               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8053             // 2 opposite quadrangles stuck with a diagonal;
8054             // sample groups of merged indices: (0-4)(2-6)
8055             // --------------------------------------------> 2 tetrahedrons
8056             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8057             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8058             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8059             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8060                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8061               // stuck with 0-2 diagonal
8062               i0  = ind1[ 3 ];
8063               i1d = ind1[ 0 ];
8064               i2  = ind1[ 1 ];
8065               i3d = ind1[ 2 ];
8066               i0t = ind2[ 1 ];
8067               i2t = ind2[ 3 ];
8068             }
8069             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8070                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8071               // stuck with 1-3 diagonal
8072               i0  = ind1[ 0 ];
8073               i1d = ind1[ 1 ];
8074               i2  = ind1[ 2 ];
8075               i3d = ind1[ 3 ];
8076               i0t = ind2[ 0 ];
8077               i2t = ind2[ 1 ];
8078             }
8079             else {
8080               ASSERT(0);
8081             }
8082             // tetrahedron 1
8083             uniqueNodes[ 0 ] = curNodes [ i0 ];
8084             uniqueNodes[ 1 ] = curNodes [ i1d ];
8085             uniqueNodes[ 2 ] = curNodes [ i3d ];
8086             uniqueNodes[ 3 ] = curNodes [ i0t ];
8087             nbUniqueNodes = 4;
8088             // tetrahedron 2
8089             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8090                                                          curNodes[ i2 ],
8091                                                          curNodes[ i3d ],
8092                                                          curNodes[ i2t ]);
8093             myLastCreatedElems.Append(newElem);
8094             if ( aShapeId )
8095               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8096             isOk = true;
8097           }
8098           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8099                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8100             // --------------------------------------------> prism
8101             // find 2 opposite triangles
8102             nbUniqueNodes = 6;
8103             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8104               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8105                 // find indices of kept and replaced nodes
8106                 // and fill unique nodes of 2 opposite triangles
8107                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8108                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8109                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8110                 // fill unique nodes
8111                 iUnique = 0;
8112                 isOk = true;
8113                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8114                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8115                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8116                   if ( n == nInit ) {
8117                     // iCur of a linked node of the opposite face (make normals co-directed):
8118                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8119                     // check that correspondent corners of triangles are linked
8120                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8121                       isOk = false;
8122                     else {
8123                       uniqueNodes[ iUnique ] = n;
8124                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8125                       iUnique++;
8126                     }
8127                   }
8128                 }
8129                 break;
8130               }
8131             }
8132           }
8133         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8134         else
8135         {
8136           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8137         }
8138         break;
8139       } // HEXAHEDRON
8140
8141       default:
8142         isOk = false;
8143       } // switch ( nbNodes )
8144
8145     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8146
8147     if ( isOk ) { // the elem remains valid after sticking nodes
8148       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8149       {
8150         // Change nodes of polyedre
8151         const SMDS_VtkVolume* aPolyedre =
8152           dynamic_cast<const SMDS_VtkVolume*>( elem );
8153         if (aPolyedre) {
8154           int nbFaces = aPolyedre->NbFaces();
8155
8156           vector<const SMDS_MeshNode *> poly_nodes;
8157           vector<int> quantities (nbFaces);
8158
8159           for (int iface = 1; iface <= nbFaces; iface++) {
8160             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8161             quantities[iface - 1] = nbFaceNodes;
8162
8163             for (inode = 1; inode <= nbFaceNodes; inode++) {
8164               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8165
8166               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8167               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8168                 curNode = (*nnIt).second;
8169               }
8170               poly_nodes.push_back(curNode);
8171             }
8172           }
8173           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8174         }
8175       }
8176       else // replace non-polyhedron elements
8177       {
8178         const SMDSAbs_ElementType etyp = elem->GetType();
8179         const int elemId               = elem->GetID();
8180         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8181         uniqueNodes.resize(nbUniqueNodes);
8182
8183         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8184
8185         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8186         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8187         if ( sm && newElem )
8188           sm->AddElement( newElem );
8189         if ( elem != newElem )
8190           ReplaceElemInGroups( elem, newElem, aMesh );
8191       }
8192     }
8193     else {
8194       // Remove invalid regular element or invalid polygon
8195       rmElemIds.push_back( elem->GetID() );
8196     }
8197
8198   } // loop on elements
8199
8200   // Remove bad elements, then equal nodes (order important)
8201
8202   Remove( rmElemIds, false );
8203   Remove( rmNodeIds, true );
8204
8205 }
8206
8207
8208 // ========================================================
8209 // class   : SortableElement
8210 // purpose : allow sorting elements basing on their nodes
8211 // ========================================================
8212 class SortableElement : public set <const SMDS_MeshElement*>
8213 {
8214 public:
8215
8216   SortableElement( const SMDS_MeshElement* theElem )
8217   {
8218     myElem = theElem;
8219     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8220     while ( nodeIt->more() )
8221       this->insert( nodeIt->next() );
8222   }
8223
8224   const SMDS_MeshElement* Get() const
8225   { return myElem; }
8226
8227   void Set(const SMDS_MeshElement* e) const
8228   { myElem = e; }
8229
8230
8231 private:
8232   mutable const SMDS_MeshElement* myElem;
8233 };
8234
8235 //=======================================================================
8236 //function : FindEqualElements
8237 //purpose  : Return list of group of elements built on the same nodes.
8238 //           Search among theElements or in the whole mesh if theElements is empty
8239 //=======================================================================
8240
8241 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8242                                          TListOfListOfElementsID & theGroupsOfElementsID)
8243 {
8244   myLastCreatedElems.Clear();
8245   myLastCreatedNodes.Clear();
8246
8247   typedef map< SortableElement, int > TMapOfNodeSet;
8248   typedef list<int> TGroupOfElems;
8249
8250   if ( theElements.empty() )
8251   { // get all elements in the mesh
8252     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8253     while ( eIt->more() )
8254       theElements.insert( theElements.end(), eIt->next());
8255   }
8256
8257   vector< TGroupOfElems > arrayOfGroups;
8258   TGroupOfElems groupOfElems;
8259   TMapOfNodeSet mapOfNodeSet;
8260
8261   TIDSortedElemSet::iterator elemIt = theElements.begin();
8262   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8263     const SMDS_MeshElement* curElem = *elemIt;
8264     SortableElement SE(curElem);
8265     int ind = -1;
8266     // check uniqueness
8267     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8268     if( !(pp.second) ) {
8269       TMapOfNodeSet::iterator& itSE = pp.first;
8270       ind = (*itSE).second;
8271       arrayOfGroups[ind].push_back(curElem->GetID());
8272     }
8273     else {
8274       groupOfElems.clear();
8275       groupOfElems.push_back(curElem->GetID());
8276       arrayOfGroups.push_back(groupOfElems);
8277       i++;
8278     }
8279   }
8280
8281   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8282   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8283     groupOfElems = *groupIt;
8284     if ( groupOfElems.size() > 1 ) {
8285       groupOfElems.sort();
8286       theGroupsOfElementsID.push_back(groupOfElems);
8287     }
8288   }
8289 }
8290
8291 //=======================================================================
8292 //function : MergeElements
8293 //purpose  : In each given group, substitute all elements by the first one.
8294 //=======================================================================
8295
8296 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8297 {
8298   myLastCreatedElems.Clear();
8299   myLastCreatedNodes.Clear();
8300
8301   typedef list<int> TListOfIDs;
8302   TListOfIDs rmElemIds; // IDs of elems to remove
8303
8304   SMESHDS_Mesh* aMesh = GetMeshDS();
8305
8306   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8307   while ( groupsIt != theGroupsOfElementsID.end() ) {
8308     TListOfIDs& aGroupOfElemID = *groupsIt;
8309     aGroupOfElemID.sort();
8310     int elemIDToKeep = aGroupOfElemID.front();
8311     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8312     aGroupOfElemID.pop_front();
8313     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8314     while ( idIt != aGroupOfElemID.end() ) {
8315       int elemIDToRemove = *idIt;
8316       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8317       // add the kept element in groups of removed one (PAL15188)
8318       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8319       rmElemIds.push_back( elemIDToRemove );
8320       ++idIt;
8321     }
8322     ++groupsIt;
8323   }
8324
8325   Remove( rmElemIds, false );
8326 }
8327
8328 //=======================================================================
8329 //function : MergeEqualElements
8330 //purpose  : Remove all but one of elements built on the same nodes.
8331 //=======================================================================
8332
8333 void SMESH_MeshEditor::MergeEqualElements()
8334 {
8335   TIDSortedElemSet aMeshElements; /* empty input ==
8336                                      to merge equal elements in the whole mesh */
8337   TListOfListOfElementsID aGroupsOfElementsID;
8338   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8339   MergeElements(aGroupsOfElementsID);
8340 }
8341
8342 //=======================================================================
8343 //function : FindFaceInSet
8344 //purpose  : Return a face having linked nodes n1 and n2 and which is
8345 //           - not in avoidSet,
8346 //           - in elemSet provided that !elemSet.empty()
8347 //           i1 and i2 optionally returns indices of n1 and n2
8348 //=======================================================================
8349
8350 const SMDS_MeshElement*
8351 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8352                                 const SMDS_MeshNode*    n2,
8353                                 const TIDSortedElemSet& elemSet,
8354                                 const TIDSortedElemSet& avoidSet,
8355                                 int*                    n1ind,
8356                                 int*                    n2ind)
8357
8358 {
8359   int i1, i2;
8360   const SMDS_MeshElement* face = 0;
8361
8362   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8363   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8364   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8365   {
8366     //MESSAGE("in while ( invElemIt->more() && !face )");
8367     const SMDS_MeshElement* elem = invElemIt->next();
8368     if (avoidSet.count( elem ))
8369       continue;
8370     if ( !elemSet.empty() && !elemSet.count( elem ))
8371       continue;
8372     // index of n1
8373     i1 = elem->GetNodeIndex( n1 );
8374     // find a n2 linked to n1
8375     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8376     for ( int di = -1; di < 2 && !face; di += 2 )
8377     {
8378       i2 = (i1+di+nbN) % nbN;
8379       if ( elem->GetNode( i2 ) == n2 )
8380         face = elem;
8381     }
8382     if ( !face && elem->IsQuadratic())
8383     {
8384       // analysis for quadratic elements using all nodes
8385       const SMDS_VtkFace* F =
8386         dynamic_cast<const SMDS_VtkFace*>(elem);
8387       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8388       // use special nodes iterator
8389       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8390       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8391       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8392       {
8393         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8394         if ( n1 == prevN && n2 == n )
8395         {
8396           face = elem;
8397         }
8398         else if ( n2 == prevN && n1 == n )
8399         {
8400           face = elem; swap( i1, i2 );
8401         }
8402         prevN = n;
8403       }
8404     }
8405   }
8406   if ( n1ind ) *n1ind = i1;
8407   if ( n2ind ) *n2ind = i2;
8408   return face;
8409 }
8410
8411 //=======================================================================
8412 //function : findAdjacentFace
8413 //purpose  :
8414 //=======================================================================
8415
8416 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8417                                                 const SMDS_MeshNode* n2,
8418                                                 const SMDS_MeshElement* elem)
8419 {
8420   TIDSortedElemSet elemSet, avoidSet;
8421   if ( elem )
8422     avoidSet.insert ( elem );
8423   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8424 }
8425
8426 //=======================================================================
8427 //function : FindFreeBorder
8428 //purpose  :
8429 //=======================================================================
8430
8431 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8432
8433 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8434                                        const SMDS_MeshNode*             theSecondNode,
8435                                        const SMDS_MeshNode*             theLastNode,
8436                                        list< const SMDS_MeshNode* > &   theNodes,
8437                                        list< const SMDS_MeshElement* >& theFaces)
8438 {
8439   if ( !theFirstNode || !theSecondNode )
8440     return false;
8441   // find border face between theFirstNode and theSecondNode
8442   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8443   if ( !curElem )
8444     return false;
8445
8446   theFaces.push_back( curElem );
8447   theNodes.push_back( theFirstNode );
8448   theNodes.push_back( theSecondNode );
8449
8450   //vector<const SMDS_MeshNode*> nodes;
8451   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8452   TIDSortedElemSet foundElems;
8453   bool needTheLast = ( theLastNode != 0 );
8454
8455   while ( nStart != theLastNode ) {
8456     if ( nStart == theFirstNode )
8457       return !needTheLast;
8458
8459     // find all free border faces sharing form nStart
8460
8461     list< const SMDS_MeshElement* > curElemList;
8462     list< const SMDS_MeshNode* > nStartList;
8463     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8464     while ( invElemIt->more() ) {
8465       const SMDS_MeshElement* e = invElemIt->next();
8466       if ( e == curElem || foundElems.insert( e ).second ) {
8467         // get nodes
8468         int iNode = 0, nbNodes = e->NbNodes();
8469         //const SMDS_MeshNode* nodes[nbNodes+1];
8470         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8471
8472         if(e->IsQuadratic()) {
8473           const SMDS_VtkFace* F =
8474             dynamic_cast<const SMDS_VtkFace*>(e);
8475           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8476           // use special nodes iterator
8477           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8478           while( anIter->more() ) {
8479             nodes[ iNode++ ] = cast2Node(anIter->next());
8480           }
8481         }
8482         else {
8483           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8484           while ( nIt->more() )
8485             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8486         }
8487         nodes[ iNode ] = nodes[ 0 ];
8488         // check 2 links
8489         for ( iNode = 0; iNode < nbNodes; iNode++ )
8490           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8491                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8492               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8493           {
8494             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8495             curElemList.push_back( e );
8496           }
8497       }
8498     }
8499     // analyse the found
8500
8501     int nbNewBorders = curElemList.size();
8502     if ( nbNewBorders == 0 ) {
8503       // no free border furthermore
8504       return !needTheLast;
8505     }
8506     else if ( nbNewBorders == 1 ) {
8507       // one more element found
8508       nIgnore = nStart;
8509       nStart = nStartList.front();
8510       curElem = curElemList.front();
8511       theFaces.push_back( curElem );
8512       theNodes.push_back( nStart );
8513     }
8514     else {
8515       // several continuations found
8516       list< const SMDS_MeshElement* >::iterator curElemIt;
8517       list< const SMDS_MeshNode* >::iterator nStartIt;
8518       // check if one of them reached the last node
8519       if ( needTheLast ) {
8520         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8521              curElemIt!= curElemList.end();
8522              curElemIt++, nStartIt++ )
8523           if ( *nStartIt == theLastNode ) {
8524             theFaces.push_back( *curElemIt );
8525             theNodes.push_back( *nStartIt );
8526             return true;
8527           }
8528       }
8529       // find the best free border by the continuations
8530       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8531       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8532       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8533            curElemIt!= curElemList.end();
8534            curElemIt++, nStartIt++ )
8535       {
8536         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8537         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8538         // find one more free border
8539         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8540           cNL->clear();
8541           cFL->clear();
8542         }
8543         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8544           // choice: clear a worse one
8545           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8546           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8547           contNodes[ iWorse ].clear();
8548           contFaces[ iWorse ].clear();
8549         }
8550       }
8551       if ( contNodes[0].empty() && contNodes[1].empty() )
8552         return false;
8553
8554       // append the best free border
8555       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8556       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8557       theNodes.pop_back(); // remove nIgnore
8558       theNodes.pop_back(); // remove nStart
8559       theFaces.pop_back(); // remove curElem
8560       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8561       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8562       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8563       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8564       return true;
8565
8566     } // several continuations found
8567   } // while ( nStart != theLastNode )
8568
8569   return true;
8570 }
8571
8572 //=======================================================================
8573 //function : CheckFreeBorderNodes
8574 //purpose  : Return true if the tree nodes are on a free border
8575 //=======================================================================
8576
8577 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8578                                             const SMDS_MeshNode* theNode2,
8579                                             const SMDS_MeshNode* theNode3)
8580 {
8581   list< const SMDS_MeshNode* > nodes;
8582   list< const SMDS_MeshElement* > faces;
8583   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8584 }
8585
8586 //=======================================================================
8587 //function : SewFreeBorder
8588 //purpose  :
8589 //=======================================================================
8590
8591 SMESH_MeshEditor::Sew_Error
8592 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8593                                  const SMDS_MeshNode* theBordSecondNode,
8594                                  const SMDS_MeshNode* theBordLastNode,
8595                                  const SMDS_MeshNode* theSideFirstNode,
8596                                  const SMDS_MeshNode* theSideSecondNode,
8597                                  const SMDS_MeshNode* theSideThirdNode,
8598                                  const bool           theSideIsFreeBorder,
8599                                  const bool           toCreatePolygons,
8600                                  const bool           toCreatePolyedrs)
8601 {
8602   myLastCreatedElems.Clear();
8603   myLastCreatedNodes.Clear();
8604
8605   MESSAGE("::SewFreeBorder()");
8606   Sew_Error aResult = SEW_OK;
8607
8608   // ====================================
8609   //    find side nodes and elements
8610   // ====================================
8611
8612   list< const SMDS_MeshNode* > nSide[ 2 ];
8613   list< const SMDS_MeshElement* > eSide[ 2 ];
8614   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8615   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8616
8617   // Free border 1
8618   // --------------
8619   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8620                       nSide[0], eSide[0])) {
8621     MESSAGE(" Free Border 1 not found " );
8622     aResult = SEW_BORDER1_NOT_FOUND;
8623   }
8624   if (theSideIsFreeBorder) {
8625     // Free border 2
8626     // --------------
8627     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8628                         nSide[1], eSide[1])) {
8629       MESSAGE(" Free Border 2 not found " );
8630       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8631     }
8632   }
8633   if ( aResult != SEW_OK )
8634     return aResult;
8635
8636   if (!theSideIsFreeBorder) {
8637     // Side 2
8638     // --------------
8639
8640     // -------------------------------------------------------------------------
8641     // Algo:
8642     // 1. If nodes to merge are not coincident, move nodes of the free border
8643     //    from the coord sys defined by the direction from the first to last
8644     //    nodes of the border to the correspondent sys of the side 2
8645     // 2. On the side 2, find the links most co-directed with the correspondent
8646     //    links of the free border
8647     // -------------------------------------------------------------------------
8648
8649     // 1. Since sewing may break if there are volumes to split on the side 2,
8650     //    we wont move nodes but just compute new coordinates for them
8651     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8652     TNodeXYZMap nBordXYZ;
8653     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8654     list< const SMDS_MeshNode* >::iterator nBordIt;
8655
8656     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8657     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8658     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8659     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8660     double tol2 = 1.e-8;
8661     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8662     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8663       // Need node movement.
8664
8665       // find X and Z axes to create trsf
8666       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8667       gp_Vec X = Zs ^ Zb;
8668       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8669         // Zb || Zs
8670         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8671
8672       // coord systems
8673       gp_Ax3 toBordAx( Pb1, Zb, X );
8674       gp_Ax3 fromSideAx( Ps1, Zs, X );
8675       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8676       // set trsf
8677       gp_Trsf toBordSys, fromSide2Sys;
8678       toBordSys.SetTransformation( toBordAx );
8679       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8680       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8681
8682       // move
8683       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8684         const SMDS_MeshNode* n = *nBordIt;
8685         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8686         toBordSys.Transforms( xyz );
8687         fromSide2Sys.Transforms( xyz );
8688         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8689       }
8690     }
8691     else {
8692       // just insert nodes XYZ in the nBordXYZ map
8693       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8694         const SMDS_MeshNode* n = *nBordIt;
8695         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8696       }
8697     }
8698
8699     // 2. On the side 2, find the links most co-directed with the correspondent
8700     //    links of the free border
8701
8702     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8703     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8704     sideNodes.push_back( theSideFirstNode );
8705
8706     bool hasVolumes = false;
8707     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8708     set<long> foundSideLinkIDs, checkedLinkIDs;
8709     SMDS_VolumeTool volume;
8710     //const SMDS_MeshNode* faceNodes[ 4 ];
8711
8712     const SMDS_MeshNode*    sideNode;
8713     const SMDS_MeshElement* sideElem;
8714     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8715     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8716     nBordIt = bordNodes.begin();
8717     nBordIt++;
8718     // border node position and border link direction to compare with
8719     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8720     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8721     // choose next side node by link direction or by closeness to
8722     // the current border node:
8723     bool searchByDir = ( *nBordIt != theBordLastNode );
8724     do {
8725       // find the next node on the Side 2
8726       sideNode = 0;
8727       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8728       long linkID;
8729       checkedLinkIDs.clear();
8730       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8731
8732       // loop on inverse elements of current node (prevSideNode) on the Side 2
8733       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8734       while ( invElemIt->more() )
8735       {
8736         const SMDS_MeshElement* elem = invElemIt->next();
8737         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8738         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8739         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8740         bool isVolume = volume.Set( elem );
8741         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8742         if ( isVolume ) // --volume
8743           hasVolumes = true;
8744         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8745           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8746           if(elem->IsQuadratic()) {
8747             const SMDS_VtkFace* F =
8748               dynamic_cast<const SMDS_VtkFace*>(elem);
8749             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8750             // use special nodes iterator
8751             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8752             while( anIter->more() ) {
8753               nodes[ iNode ] = cast2Node(anIter->next());
8754               if ( nodes[ iNode++ ] == prevSideNode )
8755                 iPrevNode = iNode - 1;
8756             }
8757           }
8758           else {
8759             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8760             while ( nIt->more() ) {
8761               nodes[ iNode ] = cast2Node( nIt->next() );
8762               if ( nodes[ iNode++ ] == prevSideNode )
8763                 iPrevNode = iNode - 1;
8764             }
8765           }
8766           // there are 2 links to check
8767           nbNodes = 2;
8768         }
8769         else // --edge
8770           continue;
8771         // loop on links, to be precise, on the second node of links
8772         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8773           const SMDS_MeshNode* n = nodes[ iNode ];
8774           if ( isVolume ) {
8775             if ( !volume.IsLinked( n, prevSideNode ))
8776               continue;
8777           }
8778           else {
8779             if ( iNode ) // a node before prevSideNode
8780               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8781             else         // a node after prevSideNode
8782               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8783           }
8784           // check if this link was already used
8785           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8786           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8787           if (!isJustChecked &&
8788               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8789           {
8790             // test a link geometrically
8791             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8792             bool linkIsBetter = false;
8793             double dot = 0.0, dist = 0.0;
8794             if ( searchByDir ) { // choose most co-directed link
8795               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8796               linkIsBetter = ( dot > maxDot );
8797             }
8798             else { // choose link with the node closest to bordPos
8799               dist = ( nextXYZ - bordPos ).SquareModulus();
8800               linkIsBetter = ( dist < minDist );
8801             }
8802             if ( linkIsBetter ) {
8803               maxDot = dot;
8804               minDist = dist;
8805               linkID = iLink;
8806               sideNode = n;
8807               sideElem = elem;
8808             }
8809           }
8810         }
8811       } // loop on inverse elements of prevSideNode
8812
8813       if ( !sideNode ) {
8814         MESSAGE(" Cant find path by links of the Side 2 ");
8815         return SEW_BAD_SIDE_NODES;
8816       }
8817       sideNodes.push_back( sideNode );
8818       sideElems.push_back( sideElem );
8819       foundSideLinkIDs.insert ( linkID );
8820       prevSideNode = sideNode;
8821
8822       if ( *nBordIt == theBordLastNode )
8823         searchByDir = false;
8824       else {
8825         // find the next border link to compare with
8826         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8827         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8828         // move to next border node if sideNode is before forward border node (bordPos)
8829         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8830           prevBordNode = *nBordIt;
8831           nBordIt++;
8832           bordPos = nBordXYZ[ *nBordIt ];
8833           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8834           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8835         }
8836       }
8837     }
8838     while ( sideNode != theSideSecondNode );
8839
8840     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8841       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8842       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8843     }
8844   } // end nodes search on the side 2
8845
8846   // ============================
8847   // sew the border to the side 2
8848   // ============================
8849
8850   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8851   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8852
8853   TListOfListOfNodes nodeGroupsToMerge;
8854   if ( nbNodes[0] == nbNodes[1] ||
8855        ( theSideIsFreeBorder && !theSideThirdNode)) {
8856
8857     // all nodes are to be merged
8858
8859     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8860          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8861          nIt[0]++, nIt[1]++ )
8862     {
8863       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8864       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8865       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8866     }
8867   }
8868   else {
8869
8870     // insert new nodes into the border and the side to get equal nb of segments
8871
8872     // get normalized parameters of nodes on the borders
8873     //double param[ 2 ][ maxNbNodes ];
8874     double* param[ 2 ];
8875     param[0] = new double [ maxNbNodes ];
8876     param[1] = new double [ maxNbNodes ];
8877     int iNode, iBord;
8878     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8879       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8880       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8881       const SMDS_MeshNode* nPrev = *nIt;
8882       double bordLength = 0;
8883       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8884         const SMDS_MeshNode* nCur = *nIt;
8885         gp_XYZ segment (nCur->X() - nPrev->X(),
8886                         nCur->Y() - nPrev->Y(),
8887                         nCur->Z() - nPrev->Z());
8888         double segmentLen = segment.Modulus();
8889         bordLength += segmentLen;
8890         param[ iBord ][ iNode ] = bordLength;
8891         nPrev = nCur;
8892       }
8893       // normalize within [0,1]
8894       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8895         param[ iBord ][ iNode ] /= bordLength;
8896       }
8897     }
8898
8899     // loop on border segments
8900     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8901     int i[ 2 ] = { 0, 0 };
8902     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8903     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8904
8905     TElemOfNodeListMap insertMap;
8906     TElemOfNodeListMap::iterator insertMapIt;
8907     // insertMap is
8908     // key:   elem to insert nodes into
8909     // value: 2 nodes to insert between + nodes to be inserted
8910     do {
8911       bool next[ 2 ] = { false, false };
8912
8913       // find min adjacent segment length after sewing
8914       double nextParam = 10., prevParam = 0;
8915       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8916         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8917           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8918         if ( i[ iBord ] > 0 )
8919           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8920       }
8921       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8922       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8923       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8924
8925       // choose to insert or to merge nodes
8926       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8927       if ( Abs( du ) <= minSegLen * 0.2 ) {
8928         // merge
8929         // ------
8930         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8931         const SMDS_MeshNode* n0 = *nIt[0];
8932         const SMDS_MeshNode* n1 = *nIt[1];
8933         nodeGroupsToMerge.back().push_back( n1 );
8934         nodeGroupsToMerge.back().push_back( n0 );
8935         // position of node of the border changes due to merge
8936         param[ 0 ][ i[0] ] += du;
8937         // move n1 for the sake of elem shape evaluation during insertion.
8938         // n1 will be removed by MergeNodes() anyway
8939         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8940         next[0] = next[1] = true;
8941       }
8942       else {
8943         // insert
8944         // ------
8945         int intoBord = ( du < 0 ) ? 0 : 1;
8946         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8947         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8948         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8949         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8950         if ( intoBord == 1 ) {
8951           // move node of the border to be on a link of elem of the side
8952           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8953           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8954           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8955           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8956           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8957         }
8958         insertMapIt = insertMap.find( elem );
8959         bool notFound = ( insertMapIt == insertMap.end() );
8960         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8961         if ( otherLink ) {
8962           // insert into another link of the same element:
8963           // 1. perform insertion into the other link of the elem
8964           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8965           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8966           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8967           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8968           // 2. perform insertion into the link of adjacent faces
8969           while (true) {
8970             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8971             if ( adjElem )
8972               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8973             else
8974               break;
8975           }
8976           if (toCreatePolyedrs) {
8977             // perform insertion into the links of adjacent volumes
8978             UpdateVolumes(n12, n22, nodeList);
8979           }
8980           // 3. find an element appeared on n1 and n2 after the insertion
8981           insertMap.erase( elem );
8982           elem = findAdjacentFace( n1, n2, 0 );
8983         }
8984         if ( notFound || otherLink ) {
8985           // add element and nodes of the side into the insertMap
8986           insertMapIt = insertMap.insert
8987             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8988           (*insertMapIt).second.push_back( n1 );
8989           (*insertMapIt).second.push_back( n2 );
8990         }
8991         // add node to be inserted into elem
8992         (*insertMapIt).second.push_back( nIns );
8993         next[ 1 - intoBord ] = true;
8994       }
8995
8996       // go to the next segment
8997       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8998         if ( next[ iBord ] ) {
8999           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9000             eIt[ iBord ]++;
9001           nPrev[ iBord ] = *nIt[ iBord ];
9002           nIt[ iBord ]++; i[ iBord ]++;
9003         }
9004       }
9005     }
9006     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9007
9008     // perform insertion of nodes into elements
9009
9010     for (insertMapIt = insertMap.begin();
9011          insertMapIt != insertMap.end();
9012          insertMapIt++ )
9013     {
9014       const SMDS_MeshElement* elem = (*insertMapIt).first;
9015       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9016       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9017       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9018
9019       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9020
9021       if ( !theSideIsFreeBorder ) {
9022         // look for and insert nodes into the faces adjacent to elem
9023         while (true) {
9024           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9025           if ( adjElem )
9026             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9027           else
9028             break;
9029         }
9030       }
9031       if (toCreatePolyedrs) {
9032         // perform insertion into the links of adjacent volumes
9033         UpdateVolumes(n1, n2, nodeList);
9034       }
9035     }
9036
9037     delete param[0];
9038     delete param[1];
9039   } // end: insert new nodes
9040
9041   MergeNodes ( nodeGroupsToMerge );
9042
9043   return aResult;
9044 }
9045
9046 //=======================================================================
9047 //function : InsertNodesIntoLink
9048 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9049 //           and theBetweenNode2 and split theElement
9050 //=======================================================================
9051
9052 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9053                                            const SMDS_MeshNode*        theBetweenNode1,
9054                                            const SMDS_MeshNode*        theBetweenNode2,
9055                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9056                                            const bool                  toCreatePoly)
9057 {
9058   if ( theFace->GetType() != SMDSAbs_Face ) return;
9059
9060   // find indices of 2 link nodes and of the rest nodes
9061   int iNode = 0, il1, il2, i3, i4;
9062   il1 = il2 = i3 = i4 = -1;
9063   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9064   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9065
9066   if(theFace->IsQuadratic()) {
9067     const SMDS_VtkFace* F =
9068       dynamic_cast<const SMDS_VtkFace*>(theFace);
9069     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9070     // use special nodes iterator
9071     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9072     while( anIter->more() ) {
9073       const SMDS_MeshNode* n = cast2Node(anIter->next());
9074       if ( n == theBetweenNode1 )
9075         il1 = iNode;
9076       else if ( n == theBetweenNode2 )
9077         il2 = iNode;
9078       else if ( i3 < 0 )
9079         i3 = iNode;
9080       else
9081         i4 = iNode;
9082       nodes[ iNode++ ] = n;
9083     }
9084   }
9085   else {
9086     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9087     while ( nodeIt->more() ) {
9088       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9089       if ( n == theBetweenNode1 )
9090         il1 = iNode;
9091       else if ( n == theBetweenNode2 )
9092         il2 = iNode;
9093       else if ( i3 < 0 )
9094         i3 = iNode;
9095       else
9096         i4 = iNode;
9097       nodes[ iNode++ ] = n;
9098     }
9099   }
9100   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9101     return ;
9102
9103   // arrange link nodes to go one after another regarding the face orientation
9104   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9105   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9106   if ( reverse ) {
9107     iNode = il1;
9108     il1 = il2;
9109     il2 = iNode;
9110     aNodesToInsert.reverse();
9111   }
9112   // check that not link nodes of a quadrangles are in good order
9113   int nbFaceNodes = theFace->NbNodes();
9114   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9115     iNode = i3;
9116     i3 = i4;
9117     i4 = iNode;
9118   }
9119
9120   if (toCreatePoly || theFace->IsPoly()) {
9121
9122     iNode = 0;
9123     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9124
9125     // add nodes of face up to first node of link
9126     bool isFLN = false;
9127
9128     if(theFace->IsQuadratic()) {
9129       const SMDS_VtkFace* F =
9130         dynamic_cast<const SMDS_VtkFace*>(theFace);
9131       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9132       // use special nodes iterator
9133       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9134       while( anIter->more()  && !isFLN ) {
9135         const SMDS_MeshNode* n = cast2Node(anIter->next());
9136         poly_nodes[iNode++] = n;
9137         if (n == nodes[il1]) {
9138           isFLN = true;
9139         }
9140       }
9141       // add nodes to insert
9142       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9143       for (; nIt != aNodesToInsert.end(); nIt++) {
9144         poly_nodes[iNode++] = *nIt;
9145       }
9146       // add nodes of face starting from last node of link
9147       while ( anIter->more() ) {
9148         poly_nodes[iNode++] = cast2Node(anIter->next());
9149       }
9150     }
9151     else {
9152       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9153       while ( nodeIt->more() && !isFLN ) {
9154         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9155         poly_nodes[iNode++] = n;
9156         if (n == nodes[il1]) {
9157           isFLN = true;
9158         }
9159       }
9160       // add nodes to insert
9161       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9162       for (; nIt != aNodesToInsert.end(); nIt++) {
9163         poly_nodes[iNode++] = *nIt;
9164       }
9165       // add nodes of face starting from last node of link
9166       while ( nodeIt->more() ) {
9167         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9168         poly_nodes[iNode++] = n;
9169       }
9170     }
9171
9172     // edit or replace the face
9173     SMESHDS_Mesh *aMesh = GetMeshDS();
9174
9175     if (theFace->IsPoly()) {
9176       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9177     }
9178     else {
9179       int aShapeId = FindShape( theFace );
9180
9181       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9182       myLastCreatedElems.Append(newElem);
9183       if ( aShapeId && newElem )
9184         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9185
9186       aMesh->RemoveElement(theFace);
9187     }
9188     return;
9189   }
9190
9191   SMESHDS_Mesh *aMesh = GetMeshDS();
9192   if( !theFace->IsQuadratic() ) {
9193
9194     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9195     int nbLinkNodes = 2 + aNodesToInsert.size();
9196     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9197     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9198     linkNodes[ 0 ] = nodes[ il1 ];
9199     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9200     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9201     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9202       linkNodes[ iNode++ ] = *nIt;
9203     }
9204     // decide how to split a quadrangle: compare possible variants
9205     // and choose which of splits to be a quadrangle
9206     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9207     if ( nbFaceNodes == 3 ) {
9208       iBestQuad = nbSplits;
9209       i4 = i3;
9210     }
9211     else if ( nbFaceNodes == 4 ) {
9212       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9213       double aBestRate = DBL_MAX;
9214       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9215         i1 = 0; i2 = 1;
9216         double aBadRate = 0;
9217         // evaluate elements quality
9218         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9219           if ( iSplit == iQuad ) {
9220             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9221                                    linkNodes[ i2++ ],
9222                                    nodes[ i3 ],
9223                                    nodes[ i4 ]);
9224             aBadRate += getBadRate( &quad, aCrit );
9225           }
9226           else {
9227             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9228                                    linkNodes[ i2++ ],
9229                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9230             aBadRate += getBadRate( &tria, aCrit );
9231           }
9232         }
9233         // choice
9234         if ( aBadRate < aBestRate ) {
9235           iBestQuad = iQuad;
9236           aBestRate = aBadRate;
9237         }
9238       }
9239     }
9240
9241     // create new elements
9242     int aShapeId = FindShape( theFace );
9243
9244     i1 = 0; i2 = 1;
9245     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9246       SMDS_MeshElement* newElem = 0;
9247       if ( iSplit == iBestQuad )
9248         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9249                                   linkNodes[ i2++ ],
9250                                   nodes[ i3 ],
9251                                   nodes[ i4 ]);
9252       else
9253         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9254                                   linkNodes[ i2++ ],
9255                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9256       myLastCreatedElems.Append(newElem);
9257       if ( aShapeId && newElem )
9258         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9259     }
9260
9261     // change nodes of theFace
9262     const SMDS_MeshNode* newNodes[ 4 ];
9263     newNodes[ 0 ] = linkNodes[ i1 ];
9264     newNodes[ 1 ] = linkNodes[ i2 ];
9265     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9266     newNodes[ 3 ] = nodes[ i4 ];
9267     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9268     const SMDS_MeshElement* newElem = 0;
9269     if (iSplit == iBestQuad)
9270       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9271     else
9272       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9273     myLastCreatedElems.Append(newElem);
9274     if ( aShapeId && newElem )
9275       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9276 } // end if(!theFace->IsQuadratic())
9277   else { // theFace is quadratic
9278     // we have to split theFace on simple triangles and one simple quadrangle
9279     int tmp = il1/2;
9280     int nbshift = tmp*2;
9281     // shift nodes in nodes[] by nbshift
9282     int i,j;
9283     for(i=0; i<nbshift; i++) {
9284       const SMDS_MeshNode* n = nodes[0];
9285       for(j=0; j<nbFaceNodes-1; j++) {
9286         nodes[j] = nodes[j+1];
9287       }
9288       nodes[nbFaceNodes-1] = n;
9289     }
9290     il1 = il1 - nbshift;
9291     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9292     //   n0      n1     n2    n0      n1     n2
9293     //     +-----+-----+        +-----+-----+
9294     //      \         /         |           |
9295     //       \       /          |           |
9296     //      n5+     +n3       n7+           +n3
9297     //         \   /            |           |
9298     //          \ /             |           |
9299     //           +              +-----+-----+
9300     //           n4           n6      n5     n4
9301
9302     // create new elements
9303     int aShapeId = FindShape( theFace );
9304
9305     int n1,n2,n3;
9306     if(nbFaceNodes==6) { // quadratic triangle
9307       SMDS_MeshElement* newElem =
9308         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9309       myLastCreatedElems.Append(newElem);
9310       if ( aShapeId && newElem )
9311         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9312       if(theFace->IsMediumNode(nodes[il1])) {
9313         // create quadrangle
9314         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9315         myLastCreatedElems.Append(newElem);
9316         if ( aShapeId && newElem )
9317           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9318         n1 = 1;
9319         n2 = 2;
9320         n3 = 3;
9321       }
9322       else {
9323         // create quadrangle
9324         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9325         myLastCreatedElems.Append(newElem);
9326         if ( aShapeId && newElem )
9327           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9328         n1 = 0;
9329         n2 = 1;
9330         n3 = 5;
9331       }
9332     }
9333     else { // nbFaceNodes==8 - quadratic quadrangle
9334       SMDS_MeshElement* newElem =
9335         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9336       myLastCreatedElems.Append(newElem);
9337       if ( aShapeId && newElem )
9338         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9339       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9340       myLastCreatedElems.Append(newElem);
9341       if ( aShapeId && newElem )
9342         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9343       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9344       myLastCreatedElems.Append(newElem);
9345       if ( aShapeId && newElem )
9346         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9347       if(theFace->IsMediumNode(nodes[il1])) {
9348         // create quadrangle
9349         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9350         myLastCreatedElems.Append(newElem);
9351         if ( aShapeId && newElem )
9352           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9353         n1 = 1;
9354         n2 = 2;
9355         n3 = 3;
9356       }
9357       else {
9358         // create quadrangle
9359         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9360         myLastCreatedElems.Append(newElem);
9361         if ( aShapeId && newElem )
9362           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9363         n1 = 0;
9364         n2 = 1;
9365         n3 = 7;
9366       }
9367     }
9368     // create needed triangles using n1,n2,n3 and inserted nodes
9369     int nbn = 2 + aNodesToInsert.size();
9370     //const SMDS_MeshNode* aNodes[nbn];
9371     vector<const SMDS_MeshNode*> aNodes(nbn);
9372     aNodes[0] = nodes[n1];
9373     aNodes[nbn-1] = nodes[n2];
9374     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9375     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9376       aNodes[iNode++] = *nIt;
9377     }
9378     for(i=1; i<nbn; i++) {
9379       SMDS_MeshElement* newElem =
9380         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9381       myLastCreatedElems.Append(newElem);
9382       if ( aShapeId && newElem )
9383         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9384     }
9385   }
9386   // remove old face
9387   aMesh->RemoveElement(theFace);
9388 }
9389
9390 //=======================================================================
9391 //function : UpdateVolumes
9392 //purpose  :
9393 //=======================================================================
9394 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9395                                       const SMDS_MeshNode*        theBetweenNode2,
9396                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9397 {
9398   myLastCreatedElems.Clear();
9399   myLastCreatedNodes.Clear();
9400
9401   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9402   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9403     const SMDS_MeshElement* elem = invElemIt->next();
9404
9405     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9406     SMDS_VolumeTool aVolume (elem);
9407     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9408       continue;
9409
9410     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9411     int iface, nbFaces = aVolume.NbFaces();
9412     vector<const SMDS_MeshNode *> poly_nodes;
9413     vector<int> quantities (nbFaces);
9414
9415     for (iface = 0; iface < nbFaces; iface++) {
9416       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9417       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9418       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9419
9420       for (int inode = 0; inode < nbFaceNodes; inode++) {
9421         poly_nodes.push_back(faceNodes[inode]);
9422
9423         if (nbInserted == 0) {
9424           if (faceNodes[inode] == theBetweenNode1) {
9425             if (faceNodes[inode + 1] == theBetweenNode2) {
9426               nbInserted = theNodesToInsert.size();
9427
9428               // add nodes to insert
9429               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9430               for (; nIt != theNodesToInsert.end(); nIt++) {
9431                 poly_nodes.push_back(*nIt);
9432               }
9433             }
9434           }
9435           else if (faceNodes[inode] == theBetweenNode2) {
9436             if (faceNodes[inode + 1] == theBetweenNode1) {
9437               nbInserted = theNodesToInsert.size();
9438
9439               // add nodes to insert in reversed order
9440               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9441               nIt--;
9442               for (; nIt != theNodesToInsert.begin(); nIt--) {
9443                 poly_nodes.push_back(*nIt);
9444               }
9445               poly_nodes.push_back(*nIt);
9446             }
9447           }
9448           else {
9449           }
9450         }
9451       }
9452       quantities[iface] = nbFaceNodes + nbInserted;
9453     }
9454
9455     // Replace or update the volume
9456     SMESHDS_Mesh *aMesh = GetMeshDS();
9457
9458     if (elem->IsPoly()) {
9459       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9460
9461     }
9462     else {
9463       int aShapeId = FindShape( elem );
9464
9465       SMDS_MeshElement* newElem =
9466         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9467       myLastCreatedElems.Append(newElem);
9468       if (aShapeId && newElem)
9469         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9470
9471       aMesh->RemoveElement(elem);
9472     }
9473   }
9474 }
9475
9476 namespace
9477 {
9478   //================================================================================
9479   /*!
9480    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9481    */
9482   //================================================================================
9483
9484   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9485                            vector<const SMDS_MeshNode *> & nodes,
9486                            vector<int> &                   nbNodeInFaces )
9487   {
9488     nodes.clear();
9489     nbNodeInFaces.clear();
9490     SMDS_VolumeTool vTool ( elem );
9491     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9492     {
9493       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9494       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9495       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9496     }
9497   }
9498 }
9499
9500 //=======================================================================
9501 /*!
9502  * \brief Convert elements contained in a submesh to quadratic
9503  * \return int - nb of checked elements
9504  */
9505 //=======================================================================
9506
9507 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9508                                              SMESH_MesherHelper& theHelper,
9509                                              const bool          theForce3d)
9510 {
9511   int nbElem = 0;
9512   if( !theSm ) return nbElem;
9513
9514   vector<int> nbNodeInFaces;
9515   vector<const SMDS_MeshNode *> nodes;
9516   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9517   while(ElemItr->more())
9518   {
9519     nbElem++;
9520     const SMDS_MeshElement* elem = ElemItr->next();
9521     if( !elem || elem->IsQuadratic() ) continue;
9522
9523     // get elem data needed to re-create it
9524     //
9525     const int id                        = elem->GetID();
9526     const int nbNodes                   = elem->NbNodes();
9527     const SMDSAbs_ElementType aType     = elem->GetType();
9528     const SMDSAbs_EntityType  aGeomType = elem->GetEntityType();
9529     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9530     if ( aGeomType == SMDSEntity_Polyhedra )
9531       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9532     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9533       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9534
9535     // remove a linear element
9536     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9537
9538     const SMDS_MeshElement* NewElem = 0;
9539
9540     switch( aType )
9541     {
9542     case SMDSAbs_Edge :
9543       {
9544         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9545         break;
9546       }
9547     case SMDSAbs_Face :
9548       {
9549         switch(nbNodes)
9550         {
9551         case 3:
9552           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9553           break;
9554         case 4:
9555           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9556           break;
9557         default:
9558           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9559           continue;
9560         }
9561         break;
9562       }
9563     case SMDSAbs_Volume :
9564       {
9565         switch( aGeomType )
9566         {
9567         case SMDSEntity_Tetra:
9568           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9569           break;
9570         case SMDSEntity_Pyramid:
9571           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9572           break;
9573         case SMDSEntity_Penta:
9574           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9575           break;
9576         case SMDSEntity_Hexa:
9577           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9578                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9579           break;
9580         case SMDSEntity_Hexagonal_Prism:
9581         default:
9582           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9583         }
9584         break;
9585       }
9586     default :
9587       continue;
9588     }
9589     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9590     if( NewElem )
9591       theSm->AddElement( NewElem );
9592   }
9593   return nbElem;
9594 }
9595
9596 //=======================================================================
9597 //function : ConvertToQuadratic
9598 //purpose  :
9599 //=======================================================================
9600
9601 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9602 {
9603   SMESHDS_Mesh* meshDS = GetMeshDS();
9604
9605   SMESH_MesherHelper aHelper(*myMesh);
9606   aHelper.SetIsQuadratic( true );
9607
9608   int nbCheckedElems = 0;
9609   if ( myMesh->HasShapeToMesh() )
9610   {
9611     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9612     {
9613       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9614       while ( smIt->more() ) {
9615         SMESH_subMesh* sm = smIt->next();
9616         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9617           aHelper.SetSubShape( sm->GetSubShape() );
9618           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9619         }
9620       }
9621     }
9622   }
9623   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9624   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9625   {
9626     SMESHDS_SubMesh *smDS = 0;
9627     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9628     while(aEdgeItr->more())
9629     {
9630       const SMDS_MeshEdge* edge = aEdgeItr->next();
9631       if(edge && !edge->IsQuadratic())
9632       {
9633         int id = edge->GetID();
9634         //MESSAGE("edge->GetID() " << id);
9635         const SMDS_MeshNode* n1 = edge->GetNode(0);
9636         const SMDS_MeshNode* n2 = edge->GetNode(1);
9637
9638         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9639
9640         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9641         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9642       }
9643     }
9644     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9645     while(aFaceItr->more())
9646     {
9647       const SMDS_MeshFace* face = aFaceItr->next();
9648       if(!face || face->IsQuadratic() ) continue;
9649
9650       const int id = face->GetID();
9651       const SMDSAbs_EntityType type = face->GetEntityType();
9652       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9653
9654       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9655
9656       SMDS_MeshFace * NewFace = 0;
9657       switch( type )
9658       {
9659       case SMDSEntity_Triangle:
9660         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9661         break;
9662       case SMDSEntity_Quadrangle:
9663         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9664         break;
9665       default:
9666         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9667       }
9668       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9669     }
9670     vector<int> nbNodeInFaces;
9671     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9672     while(aVolumeItr->more())
9673     {
9674       const SMDS_MeshVolume* volume = aVolumeItr->next();
9675       if(!volume || volume->IsQuadratic() ) continue;
9676
9677       const int id = volume->GetID();
9678       const SMDSAbs_EntityType type = volume->GetEntityType();
9679       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9680       if ( type == SMDSEntity_Polyhedra )
9681         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9682       else if ( type == SMDSEntity_Hexagonal_Prism )
9683         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9684
9685       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9686
9687       SMDS_MeshVolume * NewVolume = 0;
9688       switch ( type )
9689       {
9690       case SMDSEntity_Tetra:
9691         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9692         break;
9693       case SMDSEntity_Hexa:
9694         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9695                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9696         break;
9697       case SMDSEntity_Pyramid:
9698         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9699                                       nodes[3], nodes[4], id, theForce3d);
9700         break;
9701       case SMDSEntity_Penta:
9702         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9703                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9704         break;
9705       case SMDSEntity_Hexagonal_Prism:
9706       default:
9707         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9708       }
9709       ReplaceElemInGroups(volume, NewVolume, meshDS);
9710     }
9711   }
9712
9713   if ( !theForce3d )
9714   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9715     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9716     aHelper.FixQuadraticElements(myError);
9717   }
9718 }
9719
9720 //================================================================================
9721 /*!
9722  * \brief Makes given elements quadratic
9723  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9724  *  \param theElements - elements to make quadratic
9725  */
9726 //================================================================================
9727
9728 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9729                                           TIDSortedElemSet& theElements)
9730 {
9731   if ( theElements.empty() ) return;
9732
9733   // we believe that all theElements are of the same type
9734   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9735
9736   // get all nodes shared by theElements
9737   TIDSortedNodeSet allNodes;
9738   TIDSortedElemSet::iterator eIt = theElements.begin();
9739   for ( ; eIt != theElements.end(); ++eIt )
9740     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9741
9742   // complete theElements with elements of lower dim whose all nodes are in allNodes
9743
9744   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9745   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9746   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9747   for ( ; nIt != allNodes.end(); ++nIt )
9748   {
9749     const SMDS_MeshNode* n = *nIt;
9750     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9751     while ( invIt->more() )
9752     {
9753       const SMDS_MeshElement* e = invIt->next();
9754       if ( e->IsQuadratic() )
9755       {
9756         quadAdjacentElems[ e->GetType() ].insert( e );
9757         continue;
9758       }
9759       if ( e->GetType() >= elemType )
9760       {
9761         continue; // same type of more complex linear element
9762       }
9763
9764       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9765         continue; // e is already checked
9766
9767       // check nodes
9768       bool allIn = true;
9769       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9770       while ( nodeIt->more() && allIn )
9771         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9772       if ( allIn )
9773         theElements.insert(e );
9774     }
9775   }
9776
9777   SMESH_MesherHelper helper(*myMesh);
9778   helper.SetIsQuadratic( true );
9779
9780   // add links of quadratic adjacent elements to the helper
9781
9782   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9783     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9784           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9785     {
9786       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9787     }
9788   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9789     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9790           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9791     {
9792       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9793     }
9794   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9795     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9796           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9797     {
9798       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9799     }
9800
9801   // make quadratic elements instead of linear ones
9802
9803   SMESHDS_Mesh* meshDS = GetMeshDS();
9804   SMESHDS_SubMesh* smDS = 0;
9805   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9806   {
9807     const SMDS_MeshElement* elem = *eIt;
9808     if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9809       continue;
9810
9811     const int id                   = elem->GetID();
9812     const SMDSAbs_ElementType type = elem->GetType();
9813     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9814
9815     if ( !smDS || !smDS->Contains( elem ))
9816       smDS = meshDS->MeshElements( elem->getshapeId() );
9817     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9818
9819     SMDS_MeshElement * newElem = 0;
9820     switch( nodes.size() )
9821     {
9822     case 4: // cases for most frequently used element types go first (for optimization)
9823       if ( type == SMDSAbs_Volume )
9824         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9825       else
9826         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9827       break;
9828     case 8:
9829       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9830                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9831       break;
9832     case 3:
9833       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9834       break;
9835     case 2:
9836       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9837       break;
9838     case 5:
9839       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9840                                  nodes[4], id, theForce3d);
9841       break;
9842     case 6:
9843       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9844                                  nodes[4], nodes[5], id, theForce3d);
9845       break;
9846     default:;
9847     }
9848     ReplaceElemInGroups( elem, newElem, meshDS);
9849     if( newElem && smDS )
9850       smDS->AddElement( newElem );
9851   }
9852
9853   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9854   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9855     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9856     helper.FixQuadraticElements( myError );
9857   }
9858 }
9859
9860 //=======================================================================
9861 /*!
9862  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9863  * \return int - nb of checked elements
9864  */
9865 //=======================================================================
9866
9867 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9868                                      SMDS_ElemIteratorPtr theItr,
9869                                      const int            theShapeID)
9870 {
9871   int nbElem = 0;
9872   SMESHDS_Mesh* meshDS = GetMeshDS();
9873
9874   while( theItr->more() )
9875   {
9876     const SMDS_MeshElement* elem = theItr->next();
9877     nbElem++;
9878     if( elem && elem->IsQuadratic())
9879     {
9880       int id                    = elem->GetID();
9881       int nbCornerNodes         = elem->NbCornerNodes();
9882       SMDSAbs_ElementType aType = elem->GetType();
9883
9884       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9885
9886       //remove a quadratic element
9887       if ( !theSm || !theSm->Contains( elem ))
9888         theSm = meshDS->MeshElements( elem->getshapeId() );
9889       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9890
9891       // remove medium nodes
9892       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9893         if ( nodes[i]->NbInverseElements() == 0 )
9894           meshDS->RemoveFreeNode( nodes[i], theSm );
9895
9896       // add a linear element
9897       nodes.resize( nbCornerNodes );
9898       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9899       ReplaceElemInGroups(elem, newElem, meshDS);
9900       if( theSm && newElem )
9901         theSm->AddElement( newElem );
9902     }
9903   }
9904   return nbElem;
9905 }
9906
9907 //=======================================================================
9908 //function : ConvertFromQuadratic
9909 //purpose  :
9910 //=======================================================================
9911
9912 bool SMESH_MeshEditor::ConvertFromQuadratic()
9913 {
9914   int nbCheckedElems = 0;
9915   if ( myMesh->HasShapeToMesh() )
9916   {
9917     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9918     {
9919       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9920       while ( smIt->more() ) {
9921         SMESH_subMesh* sm = smIt->next();
9922         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9923           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9924       }
9925     }
9926   }
9927
9928   int totalNbElems =
9929     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9930   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9931   {
9932     SMESHDS_SubMesh *aSM = 0;
9933     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9934   }
9935
9936   return true;
9937 }
9938
9939 namespace
9940 {
9941   //================================================================================
9942   /*!
9943    * \brief Return true if all medium nodes of the element are in the node set
9944    */
9945   //================================================================================
9946
9947   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9948   {
9949     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9950       if ( !nodeSet.count( elem->GetNode(i) ))
9951         return false;
9952     return true;
9953   }
9954 }
9955
9956 //================================================================================
9957 /*!
9958  * \brief Makes given elements linear
9959  */
9960 //================================================================================
9961
9962 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9963 {
9964   if ( theElements.empty() ) return;
9965
9966   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9967   set<int> mediumNodeIDs;
9968   TIDSortedElemSet::iterator eIt = theElements.begin();
9969   for ( ; eIt != theElements.end(); ++eIt )
9970   {
9971     const SMDS_MeshElement* e = *eIt;
9972     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9973       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9974   }
9975
9976   // replace given elements by linear ones
9977   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9978   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9979   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9980
9981   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9982   // except those elements sharing medium nodes of quadratic element whose medium nodes
9983   // are not all in mediumNodeIDs
9984
9985   // get remaining medium nodes
9986   TIDSortedNodeSet mediumNodes;
9987   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9988   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9989     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9990       mediumNodes.insert( mediumNodes.end(), n );
9991
9992   // find more quadratic elements to convert
9993   TIDSortedElemSet moreElemsToConvert;
9994   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9995   for ( ; nIt != mediumNodes.end(); ++nIt )
9996   {
9997     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9998     while ( invIt->more() )
9999     {
10000       const SMDS_MeshElement* e = invIt->next();
10001       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10002       {
10003         // find a more complex element including e and
10004         // whose medium nodes are not in mediumNodes
10005         bool complexFound = false;
10006         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10007         {
10008           SMDS_ElemIteratorPtr invIt2 =
10009             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10010           while ( invIt2->more() )
10011           {
10012             const SMDS_MeshElement* eComplex = invIt2->next();
10013             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10014             {
10015               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10016               if ( nbCommonNodes == e->NbNodes())
10017               {
10018                 complexFound = true;
10019                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10020                 break;
10021               }
10022             }
10023           }
10024         }
10025         if ( !complexFound )
10026           moreElemsToConvert.insert( e );
10027       }
10028     }
10029   }
10030   elemIt = SMDS_ElemIteratorPtr
10031     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10032   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10033 }
10034
10035 //=======================================================================
10036 //function : SewSideElements
10037 //purpose  :
10038 //=======================================================================
10039
10040 SMESH_MeshEditor::Sew_Error
10041 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10042                                    TIDSortedElemSet&    theSide2,
10043                                    const SMDS_MeshNode* theFirstNode1,
10044                                    const SMDS_MeshNode* theFirstNode2,
10045                                    const SMDS_MeshNode* theSecondNode1,
10046                                    const SMDS_MeshNode* theSecondNode2)
10047 {
10048   myLastCreatedElems.Clear();
10049   myLastCreatedNodes.Clear();
10050
10051   MESSAGE ("::::SewSideElements()");
10052   if ( theSide1.size() != theSide2.size() )
10053     return SEW_DIFF_NB_OF_ELEMENTS;
10054
10055   Sew_Error aResult = SEW_OK;
10056   // Algo:
10057   // 1. Build set of faces representing each side
10058   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10059   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10060
10061   // =======================================================================
10062   // 1. Build set of faces representing each side:
10063   // =======================================================================
10064   // a. build set of nodes belonging to faces
10065   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10066   // c. create temporary faces representing side of volumes if correspondent
10067   //    face does not exist
10068
10069   SMESHDS_Mesh* aMesh = GetMeshDS();
10070   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10071   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10072   TIDSortedElemSet             faceSet1, faceSet2;
10073   set<const SMDS_MeshElement*> volSet1,  volSet2;
10074   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10075   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10076   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10077   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10078   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10079   int iSide, iFace, iNode;
10080
10081   list<const SMDS_MeshElement* > tempFaceList;
10082   for ( iSide = 0; iSide < 2; iSide++ ) {
10083     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10084     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10085     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10086     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10087     set<const SMDS_MeshElement*>::iterator vIt;
10088     TIDSortedElemSet::iterator eIt;
10089     set<const SMDS_MeshNode*>::iterator    nIt;
10090
10091     // check that given nodes belong to given elements
10092     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10093     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10094     int firstIndex = -1, secondIndex = -1;
10095     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10096       const SMDS_MeshElement* elem = *eIt;
10097       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10098       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10099       if ( firstIndex > -1 && secondIndex > -1 ) break;
10100     }
10101     if ( firstIndex < 0 || secondIndex < 0 ) {
10102       // we can simply return until temporary faces created
10103       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10104     }
10105
10106     // -----------------------------------------------------------
10107     // 1a. Collect nodes of existing faces
10108     //     and build set of face nodes in order to detect missing
10109     //     faces corresponding to sides of volumes
10110     // -----------------------------------------------------------
10111
10112     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10113
10114     // loop on the given element of a side
10115     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10116       //const SMDS_MeshElement* elem = *eIt;
10117       const SMDS_MeshElement* elem = *eIt;
10118       if ( elem->GetType() == SMDSAbs_Face ) {
10119         faceSet->insert( elem );
10120         set <const SMDS_MeshNode*> faceNodeSet;
10121         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10122         while ( nodeIt->more() ) {
10123           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10124           nodeSet->insert( n );
10125           faceNodeSet.insert( n );
10126         }
10127         setOfFaceNodeSet.insert( faceNodeSet );
10128       }
10129       else if ( elem->GetType() == SMDSAbs_Volume )
10130         volSet->insert( elem );
10131     }
10132     // ------------------------------------------------------------------------------
10133     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10134     // ------------------------------------------------------------------------------
10135
10136     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10137       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10138       while ( fIt->more() ) { // loop on faces sharing a node
10139         const SMDS_MeshElement* f = fIt->next();
10140         if ( faceSet->find( f ) == faceSet->end() ) {
10141           // check if all nodes are in nodeSet and
10142           // complete setOfFaceNodeSet if they are
10143           set <const SMDS_MeshNode*> faceNodeSet;
10144           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10145           bool allInSet = true;
10146           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10147             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10148             if ( nodeSet->find( n ) == nodeSet->end() )
10149               allInSet = false;
10150             else
10151               faceNodeSet.insert( n );
10152           }
10153           if ( allInSet ) {
10154             faceSet->insert( f );
10155             setOfFaceNodeSet.insert( faceNodeSet );
10156           }
10157         }
10158       }
10159     }
10160
10161     // -------------------------------------------------------------------------
10162     // 1c. Create temporary faces representing sides of volumes if correspondent
10163     //     face does not exist
10164     // -------------------------------------------------------------------------
10165
10166     if ( !volSet->empty() ) {
10167       //int nodeSetSize = nodeSet->size();
10168
10169       // loop on given volumes
10170       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10171         SMDS_VolumeTool vol (*vIt);
10172         // loop on volume faces: find free faces
10173         // --------------------------------------
10174         list<const SMDS_MeshElement* > freeFaceList;
10175         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10176           if ( !vol.IsFreeFace( iFace ))
10177             continue;
10178           // check if there is already a face with same nodes in a face set
10179           const SMDS_MeshElement* aFreeFace = 0;
10180           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10181           int nbNodes = vol.NbFaceNodes( iFace );
10182           set <const SMDS_MeshNode*> faceNodeSet;
10183           vol.GetFaceNodes( iFace, faceNodeSet );
10184           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10185           if ( isNewFace ) {
10186             // no such a face is given but it still can exist, check it
10187             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10188             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10189           }
10190           if ( !aFreeFace ) {
10191             // create a temporary face
10192             if ( nbNodes == 3 ) {
10193               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10194               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10195             }
10196             else if ( nbNodes == 4 ) {
10197               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10198               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10199             }
10200             else {
10201               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10202               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10203               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10204             }
10205             if ( aFreeFace )
10206               tempFaceList.push_back( aFreeFace );
10207           }
10208
10209           if ( aFreeFace )
10210             freeFaceList.push_back( aFreeFace );
10211
10212         } // loop on faces of a volume
10213
10214         // choose one of several free faces of a volume
10215         // --------------------------------------------
10216         if ( freeFaceList.size() > 1 ) {
10217           // choose a face having max nb of nodes shared by other elems of a side
10218           int maxNbNodes = -1;
10219           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10220           while ( fIt != freeFaceList.end() ) { // loop on free faces
10221             int nbSharedNodes = 0;
10222             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10223             while ( nodeIt->more() ) { // loop on free face nodes
10224               const SMDS_MeshNode* n =
10225                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10226               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10227               while ( invElemIt->more() ) {
10228                 const SMDS_MeshElement* e = invElemIt->next();
10229                 nbSharedNodes += faceSet->count( e );
10230                 nbSharedNodes += elemSet->count( e );
10231               }
10232             }
10233             if ( nbSharedNodes > maxNbNodes ) {
10234               maxNbNodes = nbSharedNodes;
10235               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10236             }
10237             else if ( nbSharedNodes == maxNbNodes ) {
10238               fIt++;
10239             }
10240             else {
10241               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10242             }
10243           }
10244           if ( freeFaceList.size() > 1 )
10245           {
10246             // could not choose one face, use another way
10247             // choose a face most close to the bary center of the opposite side
10248             gp_XYZ aBC( 0., 0., 0. );
10249             set <const SMDS_MeshNode*> addedNodes;
10250             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10251             eIt = elemSet2->begin();
10252             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10253               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10254               while ( nodeIt->more() ) { // loop on free face nodes
10255                 const SMDS_MeshNode* n =
10256                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10257                 if ( addedNodes.insert( n ).second )
10258                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10259               }
10260             }
10261             aBC /= addedNodes.size();
10262             double minDist = DBL_MAX;
10263             fIt = freeFaceList.begin();
10264             while ( fIt != freeFaceList.end() ) { // loop on free faces
10265               double dist = 0;
10266               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10267               while ( nodeIt->more() ) { // loop on free face nodes
10268                 const SMDS_MeshNode* n =
10269                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10270                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10271                 dist += ( aBC - p ).SquareModulus();
10272               }
10273               if ( dist < minDist ) {
10274                 minDist = dist;
10275                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10276               }
10277               else
10278                 fIt = freeFaceList.erase( fIt++ );
10279             }
10280           }
10281         } // choose one of several free faces of a volume
10282
10283         if ( freeFaceList.size() == 1 ) {
10284           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10285           faceSet->insert( aFreeFace );
10286           // complete a node set with nodes of a found free face
10287           //           for ( iNode = 0; iNode < ; iNode++ )
10288           //             nodeSet->insert( fNodes[ iNode ] );
10289         }
10290
10291       } // loop on volumes of a side
10292
10293       //       // complete a set of faces if new nodes in a nodeSet appeared
10294       //       // ----------------------------------------------------------
10295       //       if ( nodeSetSize != nodeSet->size() ) {
10296       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10297       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10298       //           while ( fIt->more() ) { // loop on faces sharing a node
10299       //             const SMDS_MeshElement* f = fIt->next();
10300       //             if ( faceSet->find( f ) == faceSet->end() ) {
10301       //               // check if all nodes are in nodeSet and
10302       //               // complete setOfFaceNodeSet if they are
10303       //               set <const SMDS_MeshNode*> faceNodeSet;
10304       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10305       //               bool allInSet = true;
10306       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10307       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10308       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10309       //                   allInSet = false;
10310       //                 else
10311       //                   faceNodeSet.insert( n );
10312       //               }
10313       //               if ( allInSet ) {
10314       //                 faceSet->insert( f );
10315       //                 setOfFaceNodeSet.insert( faceNodeSet );
10316       //               }
10317       //             }
10318       //           }
10319       //         }
10320       //       }
10321     } // Create temporary faces, if there are volumes given
10322   } // loop on sides
10323
10324   if ( faceSet1.size() != faceSet2.size() ) {
10325     // delete temporary faces: they are in reverseElements of actual nodes
10326 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10327 //    while ( tmpFaceIt->more() )
10328 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10329 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10330 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10331 //      aMesh->RemoveElement(*tmpFaceIt);
10332     MESSAGE("Diff nb of faces");
10333     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10334   }
10335
10336   // ============================================================
10337   // 2. Find nodes to merge:
10338   //              bind a node to remove to a node to put instead
10339   // ============================================================
10340
10341   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10342   if ( theFirstNode1 != theFirstNode2 )
10343     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10344   if ( theSecondNode1 != theSecondNode2 )
10345     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10346
10347   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10348   set< long > linkIdSet; // links to process
10349   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10350
10351   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10352   list< NLink > linkList[2];
10353   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10354   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10355   // loop on links in linkList; find faces by links and append links
10356   // of the found faces to linkList
10357   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10358   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10359   {
10360     NLink link[] = { *linkIt[0], *linkIt[1] };
10361     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10362     if ( !linkIdSet.count( linkID ) )
10363       continue;
10364
10365     // by links, find faces in the face sets,
10366     // and find indices of link nodes in the found faces;
10367     // in a face set, there is only one or no face sharing a link
10368     // ---------------------------------------------------------------
10369
10370     const SMDS_MeshElement* face[] = { 0, 0 };
10371     vector<const SMDS_MeshNode*> fnodes[2];
10372     int iLinkNode[2][2];
10373     TIDSortedElemSet avoidSet;
10374     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10375       const SMDS_MeshNode* n1 = link[iSide].first;
10376       const SMDS_MeshNode* n2 = link[iSide].second;
10377       //cout << "Side " << iSide << " ";
10378       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10379       // find a face by two link nodes
10380       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10381                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10382       if ( face[ iSide ])
10383       {
10384         //cout << " F " << face[ iSide]->GetID() <<endl;
10385         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10386         // put face nodes to fnodes
10387         if ( face[ iSide ]->IsQuadratic() )
10388         {
10389           // use interlaced nodes iterator
10390           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10391           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10392           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10393           while ( nIter->more() )
10394             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10395         }
10396         else
10397         {
10398           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10399                                   face[ iSide ]->end_nodes() );
10400         }
10401         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10402       }
10403     }
10404
10405     // check similarity of elements of the sides
10406     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10407       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10408       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10409         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10410       }
10411       else {
10412         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10413       }
10414       break; // do not return because it's necessary to remove tmp faces
10415     }
10416
10417     // set nodes to merge
10418     // -------------------
10419
10420     if ( face[0] && face[1] )  {
10421       const int nbNodes = face[0]->NbNodes();
10422       if ( nbNodes != face[1]->NbNodes() ) {
10423         MESSAGE("Diff nb of face nodes");
10424         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10425         break; // do not return because it s necessary to remove tmp faces
10426       }
10427       bool reverse[] = { false, false }; // order of nodes in the link
10428       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10429         // analyse link orientation in faces
10430         int i1 = iLinkNode[ iSide ][ 0 ];
10431         int i2 = iLinkNode[ iSide ][ 1 ];
10432         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10433       }
10434       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10435       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10436       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10437       {
10438         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10439                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10440       }
10441
10442       // add other links of the faces to linkList
10443       // -----------------------------------------
10444
10445       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10446         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10447         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10448         if ( !iter_isnew.second ) { // already in a set: no need to process
10449           linkIdSet.erase( iter_isnew.first );
10450         }
10451         else // new in set == encountered for the first time: add
10452         {
10453           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10454           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10455           linkList[0].push_back ( NLink( n1, n2 ));
10456           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10457         }
10458       }
10459     } // 2 faces found
10460
10461     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10462       break;
10463
10464   } // loop on link lists
10465
10466   if ( aResult == SEW_OK &&
10467        ( //linkIt[0] != linkList[0].end() ||
10468          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10469     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10470              " " << (faceSetPtr[1]->empty()));
10471     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10472   }
10473
10474   // ====================================================================
10475   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10476   // ====================================================================
10477
10478   // delete temporary faces
10479 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10480 //  while ( tmpFaceIt->more() )
10481 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10482   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10483   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10484     aMesh->RemoveElement(*tmpFaceIt);
10485
10486   if ( aResult != SEW_OK)
10487     return aResult;
10488
10489   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10490   // loop on nodes replacement map
10491   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10492   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10493     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10494       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10495       nodeIDsToRemove.push_back( nToRemove->GetID() );
10496       // loop on elements sharing nToRemove
10497       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10498       while ( invElemIt->more() ) {
10499         const SMDS_MeshElement* e = invElemIt->next();
10500         // get a new suite of nodes: make replacement
10501         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10502         vector< const SMDS_MeshNode*> nodes( nbNodes );
10503         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10504         while ( nIt->more() ) {
10505           const SMDS_MeshNode* n =
10506             static_cast<const SMDS_MeshNode*>( nIt->next() );
10507           nnIt = nReplaceMap.find( n );
10508           if ( nnIt != nReplaceMap.end() ) {
10509             nbReplaced++;
10510             n = (*nnIt).second;
10511           }
10512           nodes[ i++ ] = n;
10513         }
10514         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10515         //         elemIDsToRemove.push_back( e->GetID() );
10516         //       else
10517         if ( nbReplaced )
10518           {
10519             SMDSAbs_ElementType etyp = e->GetType();
10520             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10521             if (newElem)
10522               {
10523                 myLastCreatedElems.Append(newElem);
10524                 AddToSameGroups(newElem, e, aMesh);
10525                 int aShapeId = e->getshapeId();
10526                 if ( aShapeId )
10527                   {
10528                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10529                   }
10530               }
10531             aMesh->RemoveElement(e);
10532           }
10533       }
10534     }
10535
10536   Remove( nodeIDsToRemove, true );
10537
10538   return aResult;
10539 }
10540
10541 //================================================================================
10542 /*!
10543  * \brief Find corresponding nodes in two sets of faces
10544  * \param theSide1 - first face set
10545  * \param theSide2 - second first face
10546  * \param theFirstNode1 - a boundary node of set 1
10547  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10548  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10549  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10550  * \param nReplaceMap - output map of corresponding nodes
10551  * \return bool  - is a success or not
10552  */
10553 //================================================================================
10554
10555 #ifdef _DEBUG_
10556 //#define DEBUG_MATCHING_NODES
10557 #endif
10558
10559 SMESH_MeshEditor::Sew_Error
10560 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10561                                     set<const SMDS_MeshElement*>& theSide2,
10562                                     const SMDS_MeshNode*          theFirstNode1,
10563                                     const SMDS_MeshNode*          theFirstNode2,
10564                                     const SMDS_MeshNode*          theSecondNode1,
10565                                     const SMDS_MeshNode*          theSecondNode2,
10566                                     TNodeNodeMap &                nReplaceMap)
10567 {
10568   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10569
10570   nReplaceMap.clear();
10571   if ( theFirstNode1 != theFirstNode2 )
10572     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10573   if ( theSecondNode1 != theSecondNode2 )
10574     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10575
10576   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10577   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10578
10579   list< NLink > linkList[2];
10580   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10581   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10582
10583   // loop on links in linkList; find faces by links and append links
10584   // of the found faces to linkList
10585   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10586   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10587     NLink link[] = { *linkIt[0], *linkIt[1] };
10588     if ( linkSet.find( link[0] ) == linkSet.end() )
10589       continue;
10590
10591     // by links, find faces in the face sets,
10592     // and find indices of link nodes in the found faces;
10593     // in a face set, there is only one or no face sharing a link
10594     // ---------------------------------------------------------------
10595
10596     const SMDS_MeshElement* face[] = { 0, 0 };
10597     list<const SMDS_MeshNode*> notLinkNodes[2];
10598     //bool reverse[] = { false, false }; // order of notLinkNodes
10599     int nbNodes[2];
10600     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10601     {
10602       const SMDS_MeshNode* n1 = link[iSide].first;
10603       const SMDS_MeshNode* n2 = link[iSide].second;
10604       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10605       set< const SMDS_MeshElement* > facesOfNode1;
10606       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10607       {
10608         // during a loop of the first node, we find all faces around n1,
10609         // during a loop of the second node, we find one face sharing both n1 and n2
10610         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10611         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10612         while ( fIt->more() ) { // loop on faces sharing a node
10613           const SMDS_MeshElement* f = fIt->next();
10614           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10615               ! facesOfNode1.insert( f ).second ) // f encounters twice
10616           {
10617             if ( face[ iSide ] ) {
10618               MESSAGE( "2 faces per link " );
10619               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10620             }
10621             face[ iSide ] = f;
10622             faceSet->erase( f );
10623
10624             // get not link nodes
10625             int nbN = f->NbNodes();
10626             if ( f->IsQuadratic() )
10627               nbN /= 2;
10628             nbNodes[ iSide ] = nbN;
10629             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10630             int i1 = f->GetNodeIndex( n1 );
10631             int i2 = f->GetNodeIndex( n2 );
10632             int iEnd = nbN, iBeg = -1, iDelta = 1;
10633             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10634             if ( reverse ) {
10635               std::swap( iEnd, iBeg ); iDelta = -1;
10636             }
10637             int i = i2;
10638             while ( true ) {
10639               i += iDelta;
10640               if ( i == iEnd ) i = iBeg + iDelta;
10641               if ( i == i1 ) break;
10642               nodes.push_back ( f->GetNode( i ) );
10643             }
10644           }
10645         }
10646       }
10647     }
10648     // check similarity of elements of the sides
10649     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10650       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10651       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10652         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10653       }
10654       else {
10655         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10656       }
10657     }
10658
10659     // set nodes to merge
10660     // -------------------
10661
10662     if ( face[0] && face[1] )  {
10663       if ( nbNodes[0] != nbNodes[1] ) {
10664         MESSAGE("Diff nb of face nodes");
10665         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10666       }
10667 #ifdef DEBUG_MATCHING_NODES
10668       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10669                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10670                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10671 #endif
10672       int nbN = nbNodes[0];
10673       {
10674         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10675         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10676         for ( int i = 0 ; i < nbN - 2; ++i ) {
10677 #ifdef DEBUG_MATCHING_NODES
10678           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10679 #endif
10680           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10681         }
10682       }
10683
10684       // add other links of the face 1 to linkList
10685       // -----------------------------------------
10686
10687       const SMDS_MeshElement* f0 = face[0];
10688       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10689       for ( int i = 0; i < nbN; i++ )
10690       {
10691         const SMDS_MeshNode* n2 = f0->GetNode( i );
10692         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10693           linkSet.insert( SMESH_TLink( n1, n2 ));
10694         if ( !iter_isnew.second ) { // already in a set: no need to process
10695           linkSet.erase( iter_isnew.first );
10696         }
10697         else // new in set == encountered for the first time: add
10698         {
10699 #ifdef DEBUG_MATCHING_NODES
10700           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10701                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10702 #endif
10703           linkList[0].push_back ( NLink( n1, n2 ));
10704           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10705         }
10706         n1 = n2;
10707       }
10708     } // 2 faces found
10709   } // loop on link lists
10710
10711   return SEW_OK;
10712 }
10713
10714 //================================================================================
10715 /*!
10716   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10717   \param theElems - the list of elements (edges or faces) to be replicated
10718   The nodes for duplication could be found from these elements
10719   \param theNodesNot - list of nodes to NOT replicate
10720   \param theAffectedElems - the list of elements (cells and edges) to which the
10721   replicated nodes should be associated to.
10722   \return TRUE if operation has been completed successfully, FALSE otherwise
10723 */
10724 //================================================================================
10725
10726 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10727                                     const TIDSortedElemSet& theNodesNot,
10728                                     const TIDSortedElemSet& theAffectedElems )
10729 {
10730   myLastCreatedElems.Clear();
10731   myLastCreatedNodes.Clear();
10732
10733   if ( theElems.size() == 0 )
10734     return false;
10735
10736   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10737   if ( !aMeshDS )
10738     return false;
10739
10740   bool res = false;
10741   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10742   // duplicate elements and nodes
10743   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10744   // replce nodes by duplications
10745   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10746   return res;
10747 }
10748
10749 //================================================================================
10750 /*!
10751   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10752   \param theMeshDS - mesh instance
10753   \param theElems - the elements replicated or modified (nodes should be changed)
10754   \param theNodesNot - nodes to NOT replicate
10755   \param theNodeNodeMap - relation of old node to new created node
10756   \param theIsDoubleElem - flag os to replicate element or modify
10757   \return TRUE if operation has been completed successfully, FALSE otherwise
10758 */
10759 //================================================================================
10760
10761 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10762                                     const TIDSortedElemSet& theElems,
10763                                     const TIDSortedElemSet& theNodesNot,
10764                                     std::map< const SMDS_MeshNode*,
10765                                     const SMDS_MeshNode* >& theNodeNodeMap,
10766                                     const bool theIsDoubleElem )
10767 {
10768   MESSAGE("doubleNodes");
10769   // iterate on through element and duplicate them (by nodes duplication)
10770   bool res = false;
10771   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10772   for ( ;  elemItr != theElems.end(); ++elemItr )
10773   {
10774     const SMDS_MeshElement* anElem = *elemItr;
10775     if (!anElem)
10776       continue;
10777
10778     bool isDuplicate = false;
10779     // duplicate nodes to duplicate element
10780     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10781     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10782     int ind = 0;
10783     while ( anIter->more() )
10784     {
10785
10786       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10787       SMDS_MeshNode* aNewNode = aCurrNode;
10788       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10789         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10790       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10791       {
10792         // duplicate node
10793         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10794         theNodeNodeMap[ aCurrNode ] = aNewNode;
10795         myLastCreatedNodes.Append( aNewNode );
10796       }
10797       isDuplicate |= (aCurrNode != aNewNode);
10798       newNodes[ ind++ ] = aNewNode;
10799     }
10800     if ( !isDuplicate )
10801       continue;
10802
10803     if ( theIsDoubleElem )
10804       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10805     else
10806       {
10807       MESSAGE("ChangeElementNodes");
10808       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10809       }
10810     res = true;
10811   }
10812   return res;
10813 }
10814
10815 //================================================================================
10816 /*!
10817   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10818   \param theNodes - identifiers of nodes to be doubled
10819   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10820          nodes. If list of element identifiers is empty then nodes are doubled but
10821          they not assigned to elements
10822   \return TRUE if operation has been completed successfully, FALSE otherwise
10823 */
10824 //================================================================================
10825
10826 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10827                                     const std::list< int >& theListOfModifiedElems )
10828 {
10829   MESSAGE("DoubleNodes");
10830   myLastCreatedElems.Clear();
10831   myLastCreatedNodes.Clear();
10832
10833   if ( theListOfNodes.size() == 0 )
10834     return false;
10835
10836   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10837   if ( !aMeshDS )
10838     return false;
10839
10840   // iterate through nodes and duplicate them
10841
10842   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10843
10844   std::list< int >::const_iterator aNodeIter;
10845   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10846   {
10847     int aCurr = *aNodeIter;
10848     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10849     if ( !aNode )
10850       continue;
10851
10852     // duplicate node
10853
10854     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10855     if ( aNewNode )
10856     {
10857       anOldNodeToNewNode[ aNode ] = aNewNode;
10858       myLastCreatedNodes.Append( aNewNode );
10859     }
10860   }
10861
10862   // Create map of new nodes for modified elements
10863
10864   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10865
10866   std::list< int >::const_iterator anElemIter;
10867   for ( anElemIter = theListOfModifiedElems.begin();
10868         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10869   {
10870     int aCurr = *anElemIter;
10871     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10872     if ( !anElem )
10873       continue;
10874
10875     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10876
10877     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10878     int ind = 0;
10879     while ( anIter->more() )
10880     {
10881       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10882       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10883       {
10884         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10885         aNodeArr[ ind++ ] = aNewNode;
10886       }
10887       else
10888         aNodeArr[ ind++ ] = aCurrNode;
10889     }
10890     anElemToNodes[ anElem ] = aNodeArr;
10891   }
10892
10893   // Change nodes of elements
10894
10895   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10896     anElemToNodesIter = anElemToNodes.begin();
10897   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10898   {
10899     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10900     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10901     if ( anElem )
10902       {
10903       MESSAGE("ChangeElementNodes");
10904       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10905       }
10906   }
10907
10908   return true;
10909 }
10910
10911 namespace {
10912
10913   //================================================================================
10914   /*!
10915   \brief Check if element located inside shape
10916   \return TRUE if IN or ON shape, FALSE otherwise
10917   */
10918   //================================================================================
10919
10920   template<class Classifier>
10921   bool isInside(const SMDS_MeshElement* theElem,
10922                 Classifier&             theClassifier,
10923                 const double            theTol)
10924   {
10925     gp_XYZ centerXYZ (0, 0, 0);
10926     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10927     while (aNodeItr->more())
10928       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10929
10930     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10931     theClassifier.Perform(aPnt, theTol);
10932     TopAbs_State aState = theClassifier.State();
10933     return (aState == TopAbs_IN || aState == TopAbs_ON );
10934   }
10935
10936   //================================================================================
10937   /*!
10938    * \brief Classifier of the 3D point on the TopoDS_Face
10939    *        with interaface suitable for isInside()
10940    */
10941   //================================================================================
10942
10943   struct _FaceClassifier
10944   {
10945     Extrema_ExtPS       _extremum;
10946     BRepAdaptor_Surface _surface;
10947     TopAbs_State        _state;
10948
10949     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10950     {
10951       _extremum.Initialize( _surface,
10952                             _surface.FirstUParameter(), _surface.LastUParameter(),
10953                             _surface.FirstVParameter(), _surface.LastVParameter(),
10954                             _surface.Tolerance(), _surface.Tolerance() );
10955     }
10956     void Perform(const gp_Pnt& aPnt, double theTol)
10957     {
10958       _state = TopAbs_OUT;
10959       _extremum.Perform(aPnt);
10960       if ( _extremum.IsDone() )
10961         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10962 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10963           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10964 #else
10965           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10966 #endif
10967     }
10968     TopAbs_State State() const
10969     {
10970       return _state;
10971     }
10972   };
10973 }
10974
10975 //================================================================================
10976 /*!
10977   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10978   This method is the first step of DoubleNodeElemGroupsInRegion.
10979   \param theElems - list of groups of elements (edges or faces) to be replicated
10980   \param theNodesNot - list of groups of nodes not to replicated
10981   \param theShape - shape to detect affected elements (element which geometric center
10982          located on or inside shape).
10983          The replicated nodes should be associated to affected elements.
10984   \return groups of affected elements
10985   \sa DoubleNodeElemGroupsInRegion()
10986  */
10987 //================================================================================
10988
10989 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10990                                                    const TIDSortedElemSet& theNodesNot,
10991                                                    const TopoDS_Shape&     theShape,
10992                                                    TIDSortedElemSet&       theAffectedElems)
10993 {
10994   if ( theShape.IsNull() )
10995     return false;
10996
10997   const double aTol = Precision::Confusion();
10998   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10999   auto_ptr<_FaceClassifier>              aFaceClassifier;
11000   if ( theShape.ShapeType() == TopAbs_SOLID )
11001   {
11002     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11003     bsc3d->PerformInfinitePoint(aTol);
11004   }
11005   else if (theShape.ShapeType() == TopAbs_FACE )
11006   {
11007     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11008   }
11009
11010   // iterates on indicated elements and get elements by back references from their nodes
11011   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11012   for ( ;  elemItr != theElems.end(); ++elemItr )
11013   {
11014     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11015     if (!anElem)
11016       continue;
11017
11018     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11019     while ( nodeItr->more() )
11020     {
11021       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11022       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11023         continue;
11024       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11025       while ( backElemItr->more() )
11026       {
11027         const SMDS_MeshElement* curElem = backElemItr->next();
11028         if ( curElem && theElems.find(curElem) == theElems.end() &&
11029              ( bsc3d.get() ?
11030                isInside( curElem, *bsc3d, aTol ) :
11031                isInside( curElem, *aFaceClassifier, aTol )))
11032           theAffectedElems.insert( curElem );
11033       }
11034     }
11035   }
11036   return true;
11037 }
11038
11039 //================================================================================
11040 /*!
11041   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11042   \param theElems - group of of elements (edges or faces) to be replicated
11043   \param theNodesNot - group of nodes not to replicate
11044   \param theShape - shape to detect affected elements (element which geometric center
11045   located on or inside shape).
11046   The replicated nodes should be associated to affected elements.
11047   \return TRUE if operation has been completed successfully, FALSE otherwise
11048 */
11049 //================================================================================
11050
11051 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11052                                             const TIDSortedElemSet& theNodesNot,
11053                                             const TopoDS_Shape&     theShape )
11054 {
11055   if ( theShape.IsNull() )
11056     return false;
11057
11058   const double aTol = Precision::Confusion();
11059   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11060   auto_ptr<_FaceClassifier>              aFaceClassifier;
11061   if ( theShape.ShapeType() == TopAbs_SOLID )
11062   {
11063     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11064     bsc3d->PerformInfinitePoint(aTol);
11065   }
11066   else if (theShape.ShapeType() == TopAbs_FACE )
11067   {
11068     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11069   }
11070
11071   // iterates on indicated elements and get elements by back references from their nodes
11072   TIDSortedElemSet anAffected;
11073   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11074   for ( ;  elemItr != theElems.end(); ++elemItr )
11075   {
11076     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11077     if (!anElem)
11078       continue;
11079
11080     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11081     while ( nodeItr->more() )
11082     {
11083       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11084       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11085         continue;
11086       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11087       while ( backElemItr->more() )
11088       {
11089         const SMDS_MeshElement* curElem = backElemItr->next();
11090         if ( curElem && theElems.find(curElem) == theElems.end() &&
11091              ( bsc3d.get() ?
11092                isInside( curElem, *bsc3d, aTol ) :
11093                isInside( curElem, *aFaceClassifier, aTol )))
11094           anAffected.insert( curElem );
11095       }
11096     }
11097   }
11098   return DoubleNodes( theElems, theNodesNot, anAffected );
11099 }
11100
11101 /*!
11102  *  \brief compute an oriented angle between two planes defined by four points.
11103  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11104  *  @param p0 base of the rotation axe
11105  *  @param p1 extremity of the rotation axe
11106  *  @param g1 belongs to the first plane
11107  *  @param g2 belongs to the second plane
11108  */
11109 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11110 {
11111 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11112 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11113 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11114 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11115   gp_Vec vref(p0, p1);
11116   gp_Vec v1(p0, g1);
11117   gp_Vec v2(p0, g2);
11118   gp_Vec n1 = vref.Crossed(v1);
11119   gp_Vec n2 = vref.Crossed(v2);
11120   return n2.AngleWithRef(n1, vref);
11121 }
11122
11123 /*!
11124  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11125  * The list of groups must describe a partition of the mesh volumes.
11126  * The nodes of the internal faces at the boundaries of the groups are doubled.
11127  * In option, the internal faces are replaced by flat elements.
11128  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11129  * The flat elements are stored in groups of volumes.
11130  * @param theElems - list of groups of volumes, where a group of volume is a set of
11131  * SMDS_MeshElements sorted by Id.
11132  * @param createJointElems - if TRUE, create the elements
11133  * @return TRUE if operation has been completed successfully, FALSE otherwise
11134  */
11135 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11136                                                      bool createJointElems)
11137 {
11138   MESSAGE("----------------------------------------------");
11139   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11140   MESSAGE("----------------------------------------------");
11141
11142   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11143   meshDS->BuildDownWardConnectivity(true);
11144   CHRONO(50);
11145   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11146
11147   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11148   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11149   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11150
11151   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11152   std::map<int,int>celldom; // cell vtkId --> domain
11153   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11154   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11155   faceDomains.clear();
11156   celldom.clear();
11157   cellDomains.clear();
11158   nodeDomains.clear();
11159   std::map<int,int> emptyMap;
11160   std::set<int> emptySet;
11161   emptyMap.clear();
11162
11163   for (int idom = 0; idom < theElems.size(); idom++)
11164     {
11165
11166       // --- build a map (face to duplicate --> volume to modify)
11167       //     with all the faces shared by 2 domains (group of elements)
11168       //     and corresponding volume of this domain, for each shared face.
11169       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11170
11171       //MESSAGE("Domain " << idom);
11172       const TIDSortedElemSet& domain = theElems[idom];
11173       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11174       for (; elemItr != domain.end(); ++elemItr)
11175         {
11176           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11177           if (!anElem)
11178             continue;
11179           int vtkId = anElem->getVtkId();
11180           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11181           int neighborsVtkIds[NBMAXNEIGHBORS];
11182           int downIds[NBMAXNEIGHBORS];
11183           unsigned char downTypes[NBMAXNEIGHBORS];
11184           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11185           for (int n = 0; n < nbNeighbors; n++)
11186             {
11187               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11188               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11189               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11190                 {
11191                   DownIdType face(downIds[n], downTypes[n]);
11192                   if (!faceDomains.count(face))
11193                     faceDomains[face] = emptyMap; // create an empty entry for face
11194                   if (!faceDomains[face].count(idom))
11195                     {
11196                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11197                       celldom[vtkId] = idom;
11198                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11199                     }
11200                 }
11201             }
11202         }
11203     }
11204
11205   //MESSAGE("Number of shared faces " << faceDomains.size());
11206   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11207
11208   // --- explore the shared faces domain by domain,
11209   //     explore the nodes of the face and see if they belong to a cell in the domain,
11210   //     which has only a node or an edge on the border (not a shared face)
11211
11212   for (int idomain = 0; idomain < theElems.size(); idomain++)
11213     {
11214       //MESSAGE("Domain " << idomain);
11215       const TIDSortedElemSet& domain = theElems[idomain];
11216       itface = faceDomains.begin();
11217       for (; itface != faceDomains.end(); ++itface)
11218         {
11219           std::map<int, int> domvol = itface->second;
11220           if (!domvol.count(idomain))
11221             continue;
11222           DownIdType face = itface->first;
11223           //MESSAGE(" --- face " << face.cellId);
11224           std::set<int> oldNodes;
11225           oldNodes.clear();
11226           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11227           std::set<int>::iterator itn = oldNodes.begin();
11228           for (; itn != oldNodes.end(); ++itn)
11229             {
11230               int oldId = *itn;
11231               //MESSAGE("     node " << oldId);
11232               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11233               for (int i=0; i<l.ncells; i++)
11234                 {
11235                   int vtkId = l.cells[i];
11236                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11237                   if (!domain.count(anElem))
11238                     continue;
11239                   int vtkType = grid->GetCellType(vtkId);
11240                   int downId = grid->CellIdToDownId(vtkId);
11241                   if (downId < 0)
11242                     {
11243                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11244                       continue; // not OK at this stage of the algorithm:
11245                                 //no cells created after BuildDownWardConnectivity
11246                     }
11247                   DownIdType aCell(downId, vtkType);
11248                   if (!cellDomains.count(aCell))
11249                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11250                   cellDomains[aCell][idomain] = vtkId;
11251                   celldom[vtkId] = idomain;
11252                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11253                 }
11254             }
11255         }
11256     }
11257
11258   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11259   //     for each shared face, get the nodes
11260   //     for each node, for each domain of the face, create a clone of the node
11261
11262   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11263   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11264   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11265
11266   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11267   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11268   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11269
11270   for (int idomain = 0; idomain < theElems.size(); idomain++)
11271     {
11272       itface = faceDomains.begin();
11273       for (; itface != faceDomains.end(); ++itface)
11274         {
11275           std::map<int, int> domvol = itface->second;
11276           if (!domvol.count(idomain))
11277             continue;
11278           DownIdType face = itface->first;
11279           //MESSAGE(" --- face " << face.cellId);
11280           std::set<int> oldNodes;
11281           oldNodes.clear();
11282           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11283           std::set<int>::iterator itn = oldNodes.begin();
11284           for (; itn != oldNodes.end(); ++itn)
11285             {
11286               int oldId = *itn;
11287               //MESSAGE("-+-+-a node " << oldId);
11288               if (!nodeDomains.count(oldId))
11289                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11290               if (nodeDomains[oldId].empty())
11291                 {
11292                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11293                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11294                 }
11295               std::map<int, int>::iterator itdom = domvol.begin();
11296               for (; itdom != domvol.end(); ++itdom)
11297                 {
11298                   int idom = itdom->first;
11299                   //MESSAGE("         domain " << idom);
11300                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11301                     {
11302                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11303                         {
11304                           vector<int> orderedDoms;
11305                           //MESSAGE("multiple node " << oldId);
11306                           if (mutipleNodes.count(oldId))
11307                             orderedDoms = mutipleNodes[oldId];
11308                           else
11309                             {
11310                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11311                               for (; it != nodeDomains[oldId].end(); ++it)
11312                                 orderedDoms.push_back(it->first);
11313                             }
11314                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11315                           //stringstream txt;
11316                           //for (int i=0; i<orderedDoms.size(); i++)
11317                           //  txt << orderedDoms[i] << " ";
11318                           //MESSAGE("orderedDoms " << txt.str());
11319                           mutipleNodes[oldId] = orderedDoms;
11320                         }
11321                       double *coords = grid->GetPoint(oldId);
11322                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11323                       int newId = newNode->getVtkId();
11324                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11325                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11326                     }
11327                 }
11328             }
11329         }
11330     }
11331
11332   for (int idomain = 0; idomain < theElems.size(); idomain++)
11333     {
11334       itface = faceDomains.begin();
11335       for (; itface != faceDomains.end(); ++itface)
11336         {
11337           std::map<int, int> domvol = itface->second;
11338           if (!domvol.count(idomain))
11339             continue;
11340           DownIdType face = itface->first;
11341           //MESSAGE(" --- face " << face.cellId);
11342           std::set<int> oldNodes;
11343           oldNodes.clear();
11344           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11345           int nbMultipleNodes = 0;
11346           std::set<int>::iterator itn = oldNodes.begin();
11347           for (; itn != oldNodes.end(); ++itn)
11348             {
11349               int oldId = *itn;
11350               if (mutipleNodes.count(oldId))
11351                 nbMultipleNodes++;
11352             }
11353           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11354             {
11355               //MESSAGE("multiple Nodes detected on a shared face");
11356               int downId = itface->first.cellId;
11357               unsigned char cellType = itface->first.cellType;
11358               // --- shared edge or shared face ?
11359               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11360                 {
11361                   int nodes[3];
11362                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11363                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11364                     if (mutipleNodes.count(nodes[i]))
11365                       if (!mutipleNodesToFace.count(nodes[i]))
11366                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11367                 }
11368               else // shared face (between two volumes)
11369                 {
11370                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11371                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11372                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11373                   for (int ie =0; ie < nbEdges; ie++)
11374                     {
11375                       int nodes[3];
11376                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11377                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11378                         {
11379                           vector<int> vn0 = mutipleNodes[nodes[0]];
11380                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11381                           vector<int> doms;
11382                           for (int i0 = 0; i0 < vn0.size(); i0++)
11383                             for (int i1 = 0; i1 < vn1.size(); i1++)
11384                               if (vn0[i0] == vn1[i1])
11385                                 doms.push_back(vn0[i0]);
11386                           if (doms.size() >2)
11387                             {
11388                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11389                               double *coords = grid->GetPoint(nodes[0]);
11390                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11391                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11392                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11393                               gp_Pnt gref;
11394                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11395                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11396                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11397                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11398                               for (int id=0; id < doms.size(); id++)
11399                                 {
11400                                   int idom = doms[id];
11401                                   for (int ivol=0; ivol<nbvol; ivol++)
11402                                     {
11403                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11404                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11405                                       if (theElems[idom].count(elem))
11406                                         {
11407                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11408                                           domvol[idom] = svol;
11409                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11410                                           double values[3];
11411                                           vtkIdType npts = 0;
11412                                           vtkIdType* pts = 0;
11413                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11414                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11415                                           if (id ==0)
11416                                             {
11417                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11418                                               angleDom[idom] = 0;
11419                                             }
11420                                           else
11421                                             {
11422                                               gp_Pnt g(values[0], values[1], values[2]);
11423                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11424                                               //MESSAGE("  angle=" << angleDom[idom]);
11425                                             }
11426                                           break;
11427                                         }
11428                                     }
11429                                 }
11430                               map<double, int> sortedDom; // sort domains by angle
11431                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11432                                 sortedDom[ia->second] = ia->first;
11433                               vector<int> vnodes;
11434                               vector<int> vdom;
11435                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11436                                 {
11437                                   vdom.push_back(ib->second);
11438                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11439                                 }
11440                               for (int ino = 0; ino < nbNodes; ino++)
11441                                 vnodes.push_back(nodes[ino]);
11442                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11443                             }
11444                         }
11445                     }
11446                 }
11447             }
11448         }
11449     }
11450
11451   // --- iterate on shared faces (volumes to modify, face to extrude)
11452   //     get node id's of the face (id SMDS = id VTK)
11453   //     create flat element with old and new nodes if requested
11454
11455   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11456   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11457
11458   std::map<int, std::map<long,int> > nodeQuadDomains;
11459   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11460
11461   if (createJointElems)
11462     {
11463       int idg;
11464       string joints2DName = "joints2D";
11465       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11466       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11467       string joints3DName = "joints3D";
11468       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11469       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11470
11471       itface = faceDomains.begin();
11472       for (; itface != faceDomains.end(); ++itface)
11473         {
11474           DownIdType face = itface->first;
11475           std::set<int> oldNodes;
11476           std::set<int>::iterator itn;
11477           oldNodes.clear();
11478           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11479
11480           std::map<int, int> domvol = itface->second;
11481           std::map<int, int>::iterator itdom = domvol.begin();
11482           int dom1 = itdom->first;
11483           int vtkVolId = itdom->second;
11484           itdom++;
11485           int dom2 = itdom->first;
11486           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11487                                                              nodeQuadDomains);
11488           stringstream grpname;
11489           grpname << "j_";
11490           if (dom1 < dom2)
11491             grpname << dom1 << "_" << dom2;
11492           else
11493             grpname << dom2 << "_" << dom1;
11494           string namegrp = grpname.str();
11495           if (!mapOfJunctionGroups.count(namegrp))
11496             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11497           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11498           if (sgrp)
11499             sgrp->Add(vol->GetID());
11500           if (vol->GetType() == SMDSAbs_Volume)
11501             joints3DGrp->Add(vol->GetID());
11502           else if (vol->GetType() == SMDSAbs_Face)
11503             joints2DGrp->Add(vol->GetID());
11504         }
11505     }
11506
11507   // --- create volumes on multiple domain intersection if requested
11508   //     iterate on mutipleNodesToFace
11509   //     iterate on edgesMultiDomains
11510
11511   if (createJointElems)
11512     {
11513       // --- iterate on mutipleNodesToFace
11514
11515       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11516       for (; itn != mutipleNodesToFace.end(); ++itn)
11517         {
11518           int node = itn->first;
11519           vector<int> orderDom = itn->second;
11520           vector<vtkIdType> orderedNodes;
11521           for (int idom = 0; idom <orderDom.size(); idom++)
11522             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11523             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11524
11525             stringstream grpname;
11526             grpname << "m2j_";
11527             grpname << 0 << "_" << 0;
11528             int idg;
11529             string namegrp = grpname.str();
11530             if (!mapOfJunctionGroups.count(namegrp))
11531               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11532             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11533             if (sgrp)
11534               sgrp->Add(face->GetID());
11535         }
11536
11537       // --- iterate on edgesMultiDomains
11538
11539       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11540       for (; ite != edgesMultiDomains.end(); ++ite)
11541         {
11542           vector<int> nodes = ite->first;
11543           vector<int> orderDom = ite->second;
11544           vector<vtkIdType> orderedNodes;
11545           if (nodes.size() == 2)
11546             {
11547               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11548               for (int ino=0; ino < nodes.size(); ino++)
11549                 if (orderDom.size() == 3)
11550                   for (int idom = 0; idom <orderDom.size(); idom++)
11551                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11552                 else
11553                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11554                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11555               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11556
11557               int idg;
11558               string namegrp = "jointsMultiples";
11559               if (!mapOfJunctionGroups.count(namegrp))
11560                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11561               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11562               if (sgrp)
11563                 sgrp->Add(vol->GetID());
11564             }
11565           else
11566             {
11567               INFOS("Quadratic multiple joints not implemented");
11568               // TODO quadratic nodes
11569             }
11570         }
11571     }
11572
11573   // --- list the explicit faces and edges of the mesh that need to be modified,
11574   //     i.e. faces and edges built with one or more duplicated nodes.
11575   //     associate these faces or edges to their corresponding domain.
11576   //     only the first domain found is kept when a face or edge is shared
11577
11578   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11579   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11580   faceOrEdgeDom.clear();
11581   feDom.clear();
11582
11583   for (int idomain = 0; idomain < theElems.size(); idomain++)
11584     {
11585       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11586       for (; itnod != nodeDomains.end(); ++itnod)
11587         {
11588           int oldId = itnod->first;
11589           //MESSAGE("     node " << oldId);
11590           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11591           for (int i = 0; i < l.ncells; i++)
11592             {
11593               int vtkId = l.cells[i];
11594               int vtkType = grid->GetCellType(vtkId);
11595               int downId = grid->CellIdToDownId(vtkId);
11596               if (downId < 0)
11597                 continue; // new cells: not to be modified
11598               DownIdType aCell(downId, vtkType);
11599               int volParents[1000];
11600               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11601               for (int j = 0; j < nbvol; j++)
11602                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11603                   if (!feDom.count(vtkId))
11604                     {
11605                       feDom[vtkId] = idomain;
11606                       faceOrEdgeDom[aCell] = emptyMap;
11607                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11608                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11609                       //        << " type " << vtkType << " downId " << downId);
11610                     }
11611             }
11612         }
11613     }
11614
11615   // --- iterate on shared faces (volumes to modify, face to extrude)
11616   //     get node id's of the face
11617   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11618
11619   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11620   for (int m=0; m<3; m++)
11621     {
11622       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11623       itface = (*amap).begin();
11624       for (; itface != (*amap).end(); ++itface)
11625         {
11626           DownIdType face = itface->first;
11627           std::set<int> oldNodes;
11628           std::set<int>::iterator itn;
11629           oldNodes.clear();
11630           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11631           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11632           std::map<int, int> localClonedNodeIds;
11633
11634           std::map<int, int> domvol = itface->second;
11635           std::map<int, int>::iterator itdom = domvol.begin();
11636           for (; itdom != domvol.end(); ++itdom)
11637             {
11638               int idom = itdom->first;
11639               int vtkVolId = itdom->second;
11640               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11641               localClonedNodeIds.clear();
11642               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11643                 {
11644                   int oldId = *itn;
11645                   if (nodeDomains[oldId].count(idom))
11646                     {
11647                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11648                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11649                     }
11650                 }
11651               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11652             }
11653         }
11654     }
11655
11656   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11657   grid->BuildLinks();
11658
11659   CHRONOSTOP(50);
11660   counters::stats();
11661   return true;
11662 }
11663
11664 /*!
11665  * \brief Double nodes on some external faces and create flat elements.
11666  * Flat elements are mainly used by some types of mechanic calculations.
11667  *
11668  * Each group of the list must be constituted of faces.
11669  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11670  * @param theElems - list of groups of faces, where a group of faces is a set of
11671  * SMDS_MeshElements sorted by Id.
11672  * @return TRUE if operation has been completed successfully, FALSE otherwise
11673  */
11674 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11675 {
11676   MESSAGE("-------------------------------------------------");
11677   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11678   MESSAGE("-------------------------------------------------");
11679
11680   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11681
11682   // --- For each group of faces
11683   //     duplicate the nodes, create a flat element based on the face
11684   //     replace the nodes of the faces by their clones
11685
11686   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11687   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11688   clonedNodes.clear();
11689   intermediateNodes.clear();
11690   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11691   mapOfJunctionGroups.clear();
11692
11693   for (int idom = 0; idom < theElems.size(); idom++)
11694     {
11695       const TIDSortedElemSet& domain = theElems[idom];
11696       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11697       for (; elemItr != domain.end(); ++elemItr)
11698         {
11699           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11700           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11701           if (!aFace)
11702             continue;
11703           // MESSAGE("aFace=" << aFace->GetID());
11704           bool isQuad = aFace->IsQuadratic();
11705           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11706
11707           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11708
11709           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11710           while (nodeIt->more())
11711             {
11712               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11713               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11714               if (isMedium)
11715                 ln2.push_back(node);
11716               else
11717                 ln0.push_back(node);
11718
11719               const SMDS_MeshNode* clone = 0;
11720               if (!clonedNodes.count(node))
11721                 {
11722                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11723                   clonedNodes[node] = clone;
11724                 }
11725               else
11726                 clone = clonedNodes[node];
11727
11728               if (isMedium)
11729                 ln3.push_back(clone);
11730               else
11731                 ln1.push_back(clone);
11732
11733               const SMDS_MeshNode* inter = 0;
11734               if (isQuad && (!isMedium))
11735                 {
11736                   if (!intermediateNodes.count(node))
11737                     {
11738                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11739                       intermediateNodes[node] = inter;
11740                     }
11741                   else
11742                     inter = intermediateNodes[node];
11743                   ln4.push_back(inter);
11744                 }
11745             }
11746
11747           // --- extrude the face
11748
11749           vector<const SMDS_MeshNode*> ln;
11750           SMDS_MeshVolume* vol = 0;
11751           vtkIdType aType = aFace->GetVtkType();
11752           switch (aType)
11753           {
11754             case VTK_TRIANGLE:
11755               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11756               // MESSAGE("vol prism " << vol->GetID());
11757               ln.push_back(ln1[0]);
11758               ln.push_back(ln1[1]);
11759               ln.push_back(ln1[2]);
11760               break;
11761             case VTK_QUAD:
11762               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11763               // MESSAGE("vol hexa " << vol->GetID());
11764               ln.push_back(ln1[0]);
11765               ln.push_back(ln1[1]);
11766               ln.push_back(ln1[2]);
11767               ln.push_back(ln1[3]);
11768               break;
11769             case VTK_QUADRATIC_TRIANGLE:
11770               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11771                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11772               // MESSAGE("vol quad prism " << vol->GetID());
11773               ln.push_back(ln1[0]);
11774               ln.push_back(ln1[1]);
11775               ln.push_back(ln1[2]);
11776               ln.push_back(ln3[0]);
11777               ln.push_back(ln3[1]);
11778               ln.push_back(ln3[2]);
11779               break;
11780             case VTK_QUADRATIC_QUAD:
11781 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11782 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11783 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11784               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11785                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11786                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11787               // MESSAGE("vol quad hexa " << vol->GetID());
11788               ln.push_back(ln1[0]);
11789               ln.push_back(ln1[1]);
11790               ln.push_back(ln1[2]);
11791               ln.push_back(ln1[3]);
11792               ln.push_back(ln3[0]);
11793               ln.push_back(ln3[1]);
11794               ln.push_back(ln3[2]);
11795               ln.push_back(ln3[3]);
11796               break;
11797             case VTK_POLYGON:
11798               break;
11799             default:
11800               break;
11801           }
11802
11803           if (vol)
11804             {
11805               stringstream grpname;
11806               grpname << "jf_";
11807               grpname << idom;
11808               int idg;
11809               string namegrp = grpname.str();
11810               if (!mapOfJunctionGroups.count(namegrp))
11811                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11812               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11813               if (sgrp)
11814                 sgrp->Add(vol->GetID());
11815             }
11816
11817           // --- modify the face
11818
11819           aFace->ChangeNodes(&ln[0], ln.size());
11820         }
11821     }
11822   return true;
11823 }
11824
11825 /*!
11826  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11827  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11828  *  groups of faces to remove inside the object, (idem edges).
11829  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11830  */
11831 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11832                                       const TopoDS_Shape& theShape,
11833                                       SMESH_NodeSearcher* theNodeSearcher,
11834                                       const char* groupName,
11835                                       std::vector<double>&   nodesCoords,
11836                                       std::vector<std::vector<int> >& listOfListOfNodes)
11837 {
11838   MESSAGE("--------------------------------");
11839   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11840   MESSAGE("--------------------------------");
11841
11842   // --- zone of volumes to remove is given :
11843   //     1 either by a geom shape (one or more vertices) and a radius,
11844   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11845   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11846   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11847   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11848   //     defined by it's name.
11849
11850   SMESHDS_GroupBase* groupDS = 0;
11851   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11852   while ( groupIt->more() )
11853     {
11854       groupDS = 0;
11855       SMESH_Group * group = groupIt->next();
11856       if ( !group ) continue;
11857       groupDS = group->GetGroupDS();
11858       if ( !groupDS || groupDS->IsEmpty() ) continue;
11859       std::string grpName = group->GetName();
11860       //MESSAGE("grpName=" << grpName);
11861       if (grpName == groupName)
11862         break;
11863       else
11864         groupDS = 0;
11865     }
11866
11867   bool isNodeGroup = false;
11868   bool isNodeCoords = false;
11869   if (groupDS)
11870     {
11871       if (groupDS->GetType() != SMDSAbs_Node)
11872         return;
11873       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11874     }
11875
11876   if (nodesCoords.size() > 0)
11877     isNodeCoords = true; // a list o nodes given by their coordinates
11878   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11879
11880   // --- define groups to build
11881
11882   int idg; // --- group of SMDS volumes
11883   string grpvName = groupName;
11884   grpvName += "_vol";
11885   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11886   if (!grp)
11887     {
11888       MESSAGE("group not created " << grpvName);
11889       return;
11890     }
11891   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11892
11893   int idgs; // --- group of SMDS faces on the skin
11894   string grpsName = groupName;
11895   grpsName += "_skin";
11896   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11897   if (!grps)
11898     {
11899       MESSAGE("group not created " << grpsName);
11900       return;
11901     }
11902   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11903
11904   int idgi; // --- group of SMDS faces internal (several shapes)
11905   string grpiName = groupName;
11906   grpiName += "_internalFaces";
11907   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11908   if (!grpi)
11909     {
11910       MESSAGE("group not created " << grpiName);
11911       return;
11912     }
11913   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11914
11915   int idgei; // --- group of SMDS faces internal (several shapes)
11916   string grpeiName = groupName;
11917   grpeiName += "_internalEdges";
11918   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11919   if (!grpei)
11920     {
11921       MESSAGE("group not created " << grpeiName);
11922       return;
11923     }
11924   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11925
11926   // --- build downward connectivity
11927
11928   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11929   meshDS->BuildDownWardConnectivity(true);
11930   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11931
11932   // --- set of volumes detected inside
11933
11934   std::set<int> setOfInsideVol;
11935   std::set<int> setOfVolToCheck;
11936
11937   std::vector<gp_Pnt> gpnts;
11938   gpnts.clear();
11939
11940   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11941     {
11942       MESSAGE("group of nodes provided");
11943       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11944       while ( elemIt->more() )
11945         {
11946           const SMDS_MeshElement* elem = elemIt->next();
11947           if (!elem)
11948             continue;
11949           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11950           if (!node)
11951             continue;
11952           SMDS_MeshElement* vol = 0;
11953           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11954           while (volItr->more())
11955             {
11956               vol = (SMDS_MeshElement*)volItr->next();
11957               setOfInsideVol.insert(vol->getVtkId());
11958               sgrp->Add(vol->GetID());
11959             }
11960         }
11961     }
11962   else if (isNodeCoords)
11963     {
11964       MESSAGE("list of nodes coordinates provided");
11965       int i = 0;
11966       int k = 0;
11967       while (i < nodesCoords.size()-2)
11968         {
11969           double x = nodesCoords[i++];
11970           double y = nodesCoords[i++];
11971           double z = nodesCoords[i++];
11972           gp_Pnt p = gp_Pnt(x, y ,z);
11973           gpnts.push_back(p);
11974           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11975         }
11976     }
11977   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11978     {
11979       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11980       TopTools_IndexedMapOfShape vertexMap;
11981       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11982       gp_Pnt p = gp_Pnt(0,0,0);
11983       if (vertexMap.Extent() < 1)
11984         return;
11985
11986       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11987         {
11988           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11989           p = BRep_Tool::Pnt(vertex);
11990           gpnts.push_back(p);
11991           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11992         }
11993     }
11994
11995   if (gpnts.size() > 0)
11996     {
11997       int nodeId = 0;
11998       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11999       if (startNode)
12000         nodeId = startNode->GetID();
12001       MESSAGE("nodeId " << nodeId);
12002
12003       double radius2 = radius*radius;
12004       MESSAGE("radius2 " << radius2);
12005
12006       // --- volumes on start node
12007
12008       setOfVolToCheck.clear();
12009       SMDS_MeshElement* startVol = 0;
12010       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12011       while (volItr->more())
12012         {
12013           startVol = (SMDS_MeshElement*)volItr->next();
12014           setOfVolToCheck.insert(startVol->getVtkId());
12015         }
12016       if (setOfVolToCheck.empty())
12017         {
12018           MESSAGE("No volumes found");
12019           return;
12020         }
12021
12022       // --- starting with central volumes then their neighbors, check if they are inside
12023       //     or outside the domain, until no more new neighbor volume is inside.
12024       //     Fill the group of inside volumes
12025
12026       std::map<int, double> mapOfNodeDistance2;
12027       mapOfNodeDistance2.clear();
12028       std::set<int> setOfOutsideVol;
12029       while (!setOfVolToCheck.empty())
12030         {
12031           std::set<int>::iterator it = setOfVolToCheck.begin();
12032           int vtkId = *it;
12033           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12034           bool volInside = false;
12035           vtkIdType npts = 0;
12036           vtkIdType* pts = 0;
12037           grid->GetCellPoints(vtkId, npts, pts);
12038           for (int i=0; i<npts; i++)
12039             {
12040               double distance2 = 0;
12041               if (mapOfNodeDistance2.count(pts[i]))
12042                 {
12043                   distance2 = mapOfNodeDistance2[pts[i]];
12044                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12045                 }
12046               else
12047                 {
12048                   double *coords = grid->GetPoint(pts[i]);
12049                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12050                   distance2 = 1.E40;
12051                   for (int j=0; j<gpnts.size(); j++)
12052                     {
12053                       double d2 = aPoint.SquareDistance(gpnts[j]);
12054                       if (d2 < distance2)
12055                         {
12056                           distance2 = d2;
12057                           if (distance2 < radius2)
12058                             break;
12059                         }
12060                     }
12061                   mapOfNodeDistance2[pts[i]] = distance2;
12062                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12063                 }
12064               if (distance2 < radius2)
12065                 {
12066                   volInside = true; // one or more nodes inside the domain
12067                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12068                   break;
12069                 }
12070             }
12071           if (volInside)
12072             {
12073               setOfInsideVol.insert(vtkId);
12074               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12075               int neighborsVtkIds[NBMAXNEIGHBORS];
12076               int downIds[NBMAXNEIGHBORS];
12077               unsigned char downTypes[NBMAXNEIGHBORS];
12078               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12079               for (int n = 0; n < nbNeighbors; n++)
12080                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12081                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12082             }
12083           else
12084             {
12085               setOfOutsideVol.insert(vtkId);
12086               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12087             }
12088           setOfVolToCheck.erase(vtkId);
12089         }
12090     }
12091
12092   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12093   //     If yes, add the volume to the inside set
12094
12095   bool addedInside = true;
12096   std::set<int> setOfVolToReCheck;
12097   while (addedInside)
12098     {
12099       MESSAGE(" --------------------------- re check");
12100       addedInside = false;
12101       std::set<int>::iterator itv = setOfInsideVol.begin();
12102       for (; itv != setOfInsideVol.end(); ++itv)
12103         {
12104           int vtkId = *itv;
12105           int neighborsVtkIds[NBMAXNEIGHBORS];
12106           int downIds[NBMAXNEIGHBORS];
12107           unsigned char downTypes[NBMAXNEIGHBORS];
12108           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12109           for (int n = 0; n < nbNeighbors; n++)
12110             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12111               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12112         }
12113       setOfVolToCheck = setOfVolToReCheck;
12114       setOfVolToReCheck.clear();
12115       while  (!setOfVolToCheck.empty())
12116         {
12117           std::set<int>::iterator it = setOfVolToCheck.begin();
12118           int vtkId = *it;
12119           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12120             {
12121               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12122               int countInside = 0;
12123               int neighborsVtkIds[NBMAXNEIGHBORS];
12124               int downIds[NBMAXNEIGHBORS];
12125               unsigned char downTypes[NBMAXNEIGHBORS];
12126               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12127               for (int n = 0; n < nbNeighbors; n++)
12128                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12129                   countInside++;
12130               MESSAGE("countInside " << countInside);
12131               if (countInside > 1)
12132                 {
12133                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12134                   setOfInsideVol.insert(vtkId);
12135                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12136                   addedInside = true;
12137                 }
12138               else
12139                 setOfVolToReCheck.insert(vtkId);
12140             }
12141           setOfVolToCheck.erase(vtkId);
12142         }
12143     }
12144
12145   // --- map of Downward faces at the boundary, inside the global volume
12146   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12147   //     fill group of SMDS faces inside the volume (when several volume shapes)
12148   //     fill group of SMDS faces on the skin of the global volume (if skin)
12149
12150   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12151   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12152   std::set<int>::iterator it = setOfInsideVol.begin();
12153   for (; it != setOfInsideVol.end(); ++it)
12154     {
12155       int vtkId = *it;
12156       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12157       int neighborsVtkIds[NBMAXNEIGHBORS];
12158       int downIds[NBMAXNEIGHBORS];
12159       unsigned char downTypes[NBMAXNEIGHBORS];
12160       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12161       for (int n = 0; n < nbNeighbors; n++)
12162         {
12163           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12164           if (neighborDim == 3)
12165             {
12166               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12167                 {
12168                   DownIdType face(downIds[n], downTypes[n]);
12169                   boundaryFaces[face] = vtkId;
12170                 }
12171               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12172               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12173               if (vtkFaceId >= 0)
12174                 {
12175                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12176                   // find also the smds edges on this face
12177                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12178                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12179                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12180                   for (int i = 0; i < nbEdges; i++)
12181                     {
12182                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12183                       if (vtkEdgeId >= 0)
12184                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12185                     }
12186                 }
12187             }
12188           else if (neighborDim == 2) // skin of the volume
12189             {
12190               DownIdType face(downIds[n], downTypes[n]);
12191               skinFaces[face] = vtkId;
12192               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12193               if (vtkFaceId >= 0)
12194                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12195             }
12196         }
12197     }
12198
12199   // --- identify the edges constituting the wire of each subshape on the skin
12200   //     define polylines with the nodes of edges, equivalent to wires
12201   //     project polylines on subshapes, and partition, to get geom faces
12202
12203   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12204   std::set<int> emptySet;
12205   emptySet.clear();
12206   std::set<int> shapeIds;
12207
12208   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12209   while (itelem->more())
12210     {
12211       const SMDS_MeshElement *elem = itelem->next();
12212       int shapeId = elem->getshapeId();
12213       int vtkId = elem->getVtkId();
12214       if (!shapeIdToVtkIdSet.count(shapeId))
12215         {
12216           shapeIdToVtkIdSet[shapeId] = emptySet;
12217           shapeIds.insert(shapeId);
12218         }
12219       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12220     }
12221
12222   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12223   std::set<DownIdType, DownIdCompare> emptyEdges;
12224   emptyEdges.clear();
12225
12226   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12227   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12228     {
12229       int shapeId = itShape->first;
12230       MESSAGE(" --- Shape ID --- "<< shapeId);
12231       shapeIdToEdges[shapeId] = emptyEdges;
12232
12233       std::vector<int> nodesEdges;
12234
12235       std::set<int>::iterator its = itShape->second.begin();
12236       for (; its != itShape->second.end(); ++its)
12237         {
12238           int vtkId = *its;
12239           MESSAGE("     " << vtkId);
12240           int neighborsVtkIds[NBMAXNEIGHBORS];
12241           int downIds[NBMAXNEIGHBORS];
12242           unsigned char downTypes[NBMAXNEIGHBORS];
12243           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12244           for (int n = 0; n < nbNeighbors; n++)
12245             {
12246               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12247                 continue;
12248               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12249               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12250               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12251                 {
12252                   DownIdType edge(downIds[n], downTypes[n]);
12253                   if (!shapeIdToEdges[shapeId].count(edge))
12254                     {
12255                       shapeIdToEdges[shapeId].insert(edge);
12256                       int vtkNodeId[3];
12257                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12258                       nodesEdges.push_back(vtkNodeId[0]);
12259                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12260                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12261                     }
12262                 }
12263             }
12264         }
12265
12266       std::list<int> order;
12267       order.clear();
12268       if (nodesEdges.size() > 0)
12269         {
12270           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12271           nodesEdges[0] = -1;
12272           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12273           nodesEdges[1] = -1; // do not reuse this edge
12274           bool found = true;
12275           while (found)
12276             {
12277               int nodeTofind = order.back(); // try first to push back
12278               int i = 0;
12279               for (i = 0; i<nodesEdges.size(); i++)
12280                 if (nodesEdges[i] == nodeTofind)
12281                   break;
12282               if (i == nodesEdges.size())
12283                 found = false; // no follower found on back
12284               else
12285                 {
12286                   if (i%2) // odd ==> use the previous one
12287                     if (nodesEdges[i-1] < 0)
12288                       found = false;
12289                     else
12290                       {
12291                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12292                         nodesEdges[i-1] = -1;
12293                       }
12294                   else // even ==> use the next one
12295                     if (nodesEdges[i+1] < 0)
12296                       found = false;
12297                     else
12298                       {
12299                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12300                         nodesEdges[i+1] = -1;
12301                       }
12302                 }
12303               if (found)
12304                 continue;
12305               // try to push front
12306               found = true;
12307               nodeTofind = order.front(); // try to push front
12308               for (i = 0; i<nodesEdges.size(); i++)
12309                 if (nodesEdges[i] == nodeTofind)
12310                   break;
12311               if (i == nodesEdges.size())
12312                 {
12313                   found = false; // no predecessor found on front
12314                   continue;
12315                 }
12316               if (i%2) // odd ==> use the previous one
12317                 if (nodesEdges[i-1] < 0)
12318                   found = false;
12319                 else
12320                   {
12321                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12322                     nodesEdges[i-1] = -1;
12323                   }
12324               else // even ==> use the next one
12325                 if (nodesEdges[i+1] < 0)
12326                   found = false;
12327                 else
12328                   {
12329                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12330                     nodesEdges[i+1] = -1;
12331                   }
12332             }
12333         }
12334
12335
12336       std::vector<int> nodes;
12337       nodes.push_back(shapeId);
12338       std::list<int>::iterator itl = order.begin();
12339       for (; itl != order.end(); itl++)
12340         {
12341           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12342           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12343         }
12344       listOfListOfNodes.push_back(nodes);
12345     }
12346
12347   //     partition geom faces with blocFissure
12348   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12349   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12350
12351   return;
12352 }
12353
12354
12355 //================================================================================
12356 /*!
12357  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12358  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12359  * \return TRUE if operation has been completed successfully, FALSE otherwise
12360  */
12361 //================================================================================
12362
12363 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12364 {
12365   // iterates on volume elements and detect all free faces on them
12366   SMESHDS_Mesh* aMesh = GetMeshDS();
12367   if (!aMesh)
12368     return false;
12369   //bool res = false;
12370   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12371   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12372   while(vIt->more())
12373   {
12374     const SMDS_MeshVolume* volume = vIt->next();
12375     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12376     vTool.SetExternalNormal();
12377     //const bool isPoly = volume->IsPoly();
12378     const int iQuad = volume->IsQuadratic();
12379     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12380     {
12381       if (!vTool.IsFreeFace(iface))
12382         continue;
12383       nbFree++;
12384       vector<const SMDS_MeshNode *> nodes;
12385       int nbFaceNodes = vTool.NbFaceNodes(iface);
12386       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12387       int inode = 0;
12388       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12389         nodes.push_back(faceNodes[inode]);
12390       if (iQuad) { // add medium nodes
12391         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12392           nodes.push_back(faceNodes[inode]);
12393         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12394           nodes.push_back(faceNodes[8]);
12395       }
12396       // add new face based on volume nodes
12397       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12398         nbExisted++;
12399         continue; // face already exsist
12400       }
12401       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12402       nbCreated++;
12403     }
12404   }
12405   return ( nbFree==(nbExisted+nbCreated) );
12406 }
12407
12408 namespace
12409 {
12410   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12411   {
12412     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12413       return n;
12414     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12415   }
12416 }
12417 //================================================================================
12418 /*!
12419  * \brief Creates missing boundary elements
12420  *  \param elements - elements whose boundary is to be checked
12421  *  \param dimension - defines type of boundary elements to create
12422  *  \param group - a group to store created boundary elements in
12423  *  \param targetMesh - a mesh to store created boundary elements in
12424  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12425  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12426  *                                boundary elements will be copied into the targetMesh
12427  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12428  *                                boundary elements will be added into the new group
12429  *  \param aroundElements - if true, elements will be created on boundary of given
12430  *                          elements else, on boundary of the whole mesh.
12431  * \return nb of added boundary elements
12432  */
12433 //================================================================================
12434
12435 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12436                                        Bnd_Dimension           dimension,
12437                                        SMESH_Group*            group/*=0*/,
12438                                        SMESH_Mesh*             targetMesh/*=0*/,
12439                                        bool                    toCopyElements/*=false*/,
12440                                        bool                    toCopyExistingBoundary/*=false*/,
12441                                        bool                    toAddExistingBondary/*= false*/,
12442                                        bool                    aroundElements/*= false*/)
12443 {
12444   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12445   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12446   // hope that all elements are of the same type, do not check them all
12447   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12448     throw SALOME_Exception(LOCALIZED("wrong element type"));
12449
12450   if ( !targetMesh )
12451     toCopyElements = toCopyExistingBoundary = false;
12452
12453   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12454   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12455   int nbAddedBnd = 0;
12456
12457   // editor adding present bnd elements and optionally holding elements to add to the group
12458   SMESH_MeshEditor* presentEditor;
12459   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12460   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12461
12462   SMESH_MesherHelper helper( *myMesh );
12463   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12464   SMDS_VolumeTool vTool;
12465   TIDSortedElemSet avoidSet;
12466   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12467   int inode;
12468
12469   typedef vector<const SMDS_MeshNode*> TConnectivity;
12470
12471   SMDS_ElemIteratorPtr eIt;
12472   if (elements.empty())
12473     eIt = aMesh->elementsIterator(elemType);
12474   else
12475     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12476
12477   while (eIt->more())
12478   {
12479     const SMDS_MeshElement* elem = eIt->next();
12480     const int iQuad = elem->IsQuadratic();
12481
12482     // ------------------------------------------------------------------------------------
12483     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12484     // ------------------------------------------------------------------------------------
12485     vector<const SMDS_MeshElement*> presentBndElems;
12486     vector<TConnectivity>           missingBndElems;
12487     TConnectivity nodes;
12488     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12489     {
12490       vTool.SetExternalNormal();
12491       const SMDS_MeshElement* otherVol = 0;
12492       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12493       {
12494         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12495              ( !aroundElements || elements.count( otherVol )))
12496           continue;
12497         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12498         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12499         if ( missType == SMDSAbs_Edge ) // boundary edges
12500         {
12501           nodes.resize( 2+iQuad );
12502           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12503           {
12504             for ( int j = 0; j < nodes.size(); ++j )
12505               nodes[j] =nn[i+j];
12506             if ( const SMDS_MeshElement* edge =
12507                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12508               presentBndElems.push_back( edge );
12509             else
12510               missingBndElems.push_back( nodes );
12511           }
12512         }
12513         else // boundary face
12514         {
12515           nodes.clear();
12516           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12517             nodes.push_back( nn[inode] );
12518           if (iQuad) // add medium nodes
12519             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12520               nodes.push_back( nn[inode] );
12521           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12522           if ( iCenter > 0 )
12523             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12524
12525           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12526                                                                SMDSAbs_Face, /*noMedium=*/false ))
12527             presentBndElems.push_back( f );
12528           else
12529             missingBndElems.push_back( nodes );
12530
12531           if ( targetMesh != myMesh )
12532           {
12533             // add 1D elements on face boundary to be added to a new mesh
12534             const SMDS_MeshElement* edge;
12535             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12536             {
12537               if ( iQuad )
12538                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12539               else
12540                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12541               if ( edge && avoidSet.insert( edge ).second )
12542                 presentBndElems.push_back( edge );
12543             }
12544           }
12545         }
12546       }
12547     }
12548     else                     // elem is a face ------------------------------------------
12549     {
12550       avoidSet.clear(), avoidSet.insert( elem );
12551       int nbNodes = elem->NbCornerNodes();
12552       nodes.resize( 2 /*+ iQuad*/);
12553       for ( int i = 0; i < nbNodes; i++ )
12554       {
12555         nodes[0] = elem->GetNode(i);
12556         nodes[1] = elem->GetNode((i+1)%nbNodes);
12557         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12558           continue; // not free link
12559
12560         //if ( iQuad )
12561         //nodes[2] = elem->GetNode( i + nbNodes );
12562         if ( const SMDS_MeshElement* edge =
12563              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12564           presentBndElems.push_back( edge );
12565         else
12566           missingBndElems.push_back( nodes );
12567       }
12568     }
12569
12570     // ---------------------------------
12571     // 2. Add missing boundary elements
12572     // ---------------------------------
12573     if ( targetMesh != myMesh )
12574       // instead of making a map of nodes in this mesh and targetMesh,
12575       // we create nodes with same IDs.
12576       for ( int i = 0; i < missingBndElems.size(); ++i )
12577       {
12578         TConnectivity& srcNodes = missingBndElems[i];
12579         TConnectivity  nodes( srcNodes.size() );
12580         for ( inode = 0; inode < nodes.size(); ++inode )
12581           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12582         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12583                                                                    missType,
12584                                                                    /*noMedium=*/false))
12585           continue;
12586         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12587         ++nbAddedBnd;
12588       }
12589     else
12590       for ( int i = 0; i < missingBndElems.size(); ++i )
12591       {
12592         TConnectivity& nodes = missingBndElems[i];
12593         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12594                                                                    missType,
12595                                                                    /*noMedium=*/false))
12596           continue;
12597         SMDS_MeshElement* elem =
12598           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12599         ++nbAddedBnd;
12600
12601         // try to set a new element to a shape
12602         if ( myMesh->HasShapeToMesh() )
12603         {
12604           bool ok = true;
12605           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12606           const int nbN = nodes.size() / (iQuad+1 );
12607           for ( inode = 0; inode < nbN && ok; ++inode )
12608           {
12609             pair<int, TopAbs_ShapeEnum> i_stype =
12610               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12611             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12612               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12613           }
12614           if ( ok && mediumShapes.size() > 1 )
12615           {
12616             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12617             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12618             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12619             {
12620               if (( ok = ( stype_i->first != stype_i_0.first )))
12621                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12622                                         aMesh->IndexToShape( stype_i_0.second ));
12623             }
12624           }
12625           if ( ok && mediumShapes.begin()->first == missShapeType )
12626             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12627         }
12628       }
12629
12630     // ----------------------------------
12631     // 3. Copy present boundary elements
12632     // ----------------------------------
12633     if ( toCopyExistingBoundary )
12634       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12635       {
12636         const SMDS_MeshElement* e = presentBndElems[i];
12637         TConnectivity nodes( e->NbNodes() );
12638         for ( inode = 0; inode < nodes.size(); ++inode )
12639           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12640         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12641       }
12642     else // store present elements to add them to a group
12643       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12644       {
12645         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12646       }
12647
12648   } // loop on given elements
12649
12650   // ---------------------------------------------
12651   // 4. Fill group with boundary elements
12652   // ---------------------------------------------
12653   if ( group )
12654   {
12655     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12656       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12657         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12658   }
12659   tgtEditor.myLastCreatedElems.Clear();
12660   tgtEditor2.myLastCreatedElems.Clear();
12661
12662   // -----------------------
12663   // 5. Copy given elements
12664   // -----------------------
12665   if ( toCopyElements && targetMesh != myMesh )
12666   {
12667     if (elements.empty())
12668       eIt = aMesh->elementsIterator(elemType);
12669     else
12670       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12671     while (eIt->more())
12672     {
12673       const SMDS_MeshElement* elem = eIt->next();
12674       TConnectivity nodes( elem->NbNodes() );
12675       for ( inode = 0; inode < nodes.size(); ++inode )
12676         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12677       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12678
12679       tgtEditor.myLastCreatedElems.Clear();
12680     }
12681   }
12682   return nbAddedBnd;
12683 }