Salome HOME
1cf9b4fc01776d7b8b15e04b50cd1b3b79f2e192
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Face.hxx>
76 #include <TopoDS_Solid.hxx>
77 #include <gp.hxx>
78 #include <gp_Ax1.hxx>
79 #include <gp_Dir.hxx>
80 #include <gp_Lin.hxx>
81 #include <gp_Pln.hxx>
82 #include <gp_Trsf.hxx>
83 #include <gp_Vec.hxx>
84 #include <gp_XY.hxx>
85 #include <gp_XYZ.hxx>
86
87 #include <cmath>
88
89 #include <map>
90 #include <set>
91 #include <numeric>
92 #include <limits>
93 #include <algorithm>
94 #include <sstream>
95
96 #include <boost/tuple/tuple.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
102
103 using namespace std;
104 using namespace SMESH::Controls;
105
106 namespace
107 {
108   template < class ELEM_SET >
109   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
110   {
111     typedef SMDS_SetIterator
112       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
114   }
115 }
116
117 //=======================================================================
118 //function : SMESH_MeshEditor
119 //purpose  :
120 //=======================================================================
121
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123   :myMesh( theMesh ) // theMesh may be NULL
124 {
125 }
126
127 //================================================================================
128 /*!
129  * \brief Clears myLastCreatedNodes and myLastCreatedElems
130  */
131 //================================================================================
132
133 void SMESH_MeshEditor::CrearLastCreated()
134 {
135   myLastCreatedNodes.Clear();
136   myLastCreatedElems.Clear();
137 }
138
139
140 //=======================================================================
141 /*!
142  * \brief Add element
143  */
144 //=======================================================================
145
146 SMDS_MeshElement*
147 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
148                              const SMDSAbs_ElementType            type,
149                              const bool                           isPoly,
150                              const int                            ID,
151                              const double                         ballDiameter)
152 {
153   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
154   SMDS_MeshElement* e = 0;
155   int nbnode = node.size();
156   SMESHDS_Mesh* mesh = GetMeshDS();
157   switch ( type ) {
158   case SMDSAbs_Face:
159     if ( !isPoly ) {
160       if      (nbnode == 3) {
161         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
162         else           e = mesh->AddFace      (node[0], node[1], node[2] );
163       }
164       else if (nbnode == 4) {
165         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
166         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
167       }
168       else if (nbnode == 6) {
169         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
170                                                node[4], node[5], ID);
171         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
172                                                node[4], node[5] );
173       }
174       else if (nbnode == 7) {
175         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
176                                                node[4], node[5], node[6], ID);
177         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
178                                                node[4], node[5], node[6] );
179       }
180       else if (nbnode == 8) {
181         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
182                                                node[4], node[5], node[6], node[7], ID);
183         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
184                                                node[4], node[5], node[6], node[7] );
185       }
186       else if (nbnode == 9) {
187         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
188                                                node[4], node[5], node[6], node[7], node[8], ID);
189         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
190                                                node[4], node[5], node[6], node[7], node[8] );
191       }
192     } else {
193       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
194       else           e = mesh->AddPolygonalFace      (node    );
195     }
196     break;
197
198   case SMDSAbs_Volume:
199     if ( !isPoly ) {
200       if      (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 5) {
205         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
206                                                  node[4], ID);
207         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
208                                                  node[4] );
209       }
210       else if (nbnode == 6) {
211         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], ID);
213         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
214                                                  node[4], node[5] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
218                                                  node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
220                                                  node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 10) {
223         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224                                                  node[4], node[5], node[6], node[7],
225                                                  node[8], node[9], ID);
226         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
227                                                  node[4], node[5], node[6], node[7],
228                                                  node[8], node[9] );
229       }
230       else if (nbnode == 12) {
231         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232                                                  node[4], node[5], node[6], node[7],
233                                                  node[8], node[9], node[10], node[11], ID);
234         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
235                                                  node[4], node[5], node[6], node[7],
236                                                  node[8], node[9], node[10], node[11] );
237       }
238       else if (nbnode == 13) {
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],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] );
247       }
248       else if (nbnode == 15) {
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],ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], node[6], node[7],
255                                                  node[8], node[9], node[10],node[11],
256                                                  node[12],node[13],node[14] );
257       }
258       else if (nbnode == 20) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7],
261                                                  node[8], node[9], node[10],node[11],
262                                                  node[12],node[13],node[14],node[15],
263                                                  node[16],node[17],node[18],node[19],ID);
264         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], node[10],node[11],
267                                                  node[12],node[13],node[14],node[15],
268                                                  node[16],node[17],node[18],node[19] );
269       }
270       else if (nbnode == 27) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9], node[10],node[11],
274                                                  node[12],node[13],node[14],node[15],
275                                                  node[16],node[17],node[18],node[19],
276                                                  node[20],node[21],node[22],node[23],
277                                                  node[24],node[25],node[26], ID);
278         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9], node[10],node[11],
281                                                  node[12],node[13],node[14],node[15],
282                                                  node[16],node[17],node[18],node[19],
283                                                  node[20],node[21],node[22],node[23],
284                                                  node[24],node[25],node[26] );
285       }
286     }
287     break;
288
289   case SMDSAbs_Edge:
290     if ( nbnode == 2 ) {
291       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
292       else           e = mesh->AddEdge      (node[0], node[1] );
293     }
294     else if ( nbnode == 3 ) {
295       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
296       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
297     }
298     break;
299
300   case SMDSAbs_0DElement:
301     if ( nbnode == 1 ) {
302       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
303       else           e = mesh->Add0DElement      (node[0] );
304     }
305     break;
306
307   case SMDSAbs_Node:
308     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
309     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
310     break;
311
312   case SMDSAbs_Ball:
313     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
314     else           e = mesh->AddBall      (node[0], ballDiameter);
315     break;
316
317   default:;
318   }
319   if ( e ) myLastCreatedElems.Append( e );
320   return e;
321 }
322
323 //=======================================================================
324 /*!
325  * \brief Add element
326  */
327 //=======================================================================
328
329 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
330                                                const SMDSAbs_ElementType type,
331                                                const bool                isPoly,
332                                                const int                 ID)
333 {
334   vector<const SMDS_MeshNode*> nodes;
335   nodes.reserve( nodeIDs.size() );
336   vector<int>::const_iterator id = nodeIDs.begin();
337   while ( id != nodeIDs.end() ) {
338     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
339       nodes.push_back( node );
340     else
341       return 0;
342   }
343   return AddElement( nodes, type, isPoly, ID );
344 }
345
346 //=======================================================================
347 //function : Remove
348 //purpose  : Remove a node or an element.
349 //           Modify a compute state of sub-meshes which become empty
350 //=======================================================================
351
352 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
353                               const bool         isNodes )
354 {
355   myLastCreatedElems.Clear();
356   myLastCreatedNodes.Clear();
357
358   SMESHDS_Mesh* aMesh = GetMeshDS();
359   set< SMESH_subMesh *> smmap;
360
361   int removed = 0;
362   list<int>::const_iterator it = theIDs.begin();
363   for ( ; it != theIDs.end(); it++ ) {
364     const SMDS_MeshElement * elem;
365     if ( isNodes )
366       elem = aMesh->FindNode( *it );
367     else
368       elem = aMesh->FindElement( *it );
369     if ( !elem )
370       continue;
371
372     // Notify VERTEX sub-meshes about modification
373     if ( isNodes ) {
374       const SMDS_MeshNode* node = cast2Node( elem );
375       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
376         if ( int aShapeID = node->getshapeId() )
377           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378             smmap.insert( sm );
379     }
380     // Find sub-meshes to notify about modification
381     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
382     //     while ( nodeIt->more() ) {
383     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
384     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
385     //       if ( aPosition.get() ) {
386     //         if ( int aShapeID = aPosition->GetShapeId() ) {
387     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
388     //             smmap.insert( sm );
389     //         }
390     //       }
391     //     }
392
393     // Do remove
394     if ( isNodes )
395       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
396     else
397       aMesh->RemoveElement( elem );
398     removed++;
399   }
400
401   // Notify sub-meshes about modification
402   if ( !smmap.empty() ) {
403     set< SMESH_subMesh *>::iterator smIt;
404     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
405       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
406   }
407
408   //   // Check if the whole mesh becomes empty
409   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
410   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
411
412   return removed;
413 }
414
415 //================================================================================
416 /*!
417  * \brief Create 0D elements on all nodes of the given object except those
418  *        nodes on which a 0D element already exists.
419  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
420  *                    the all mesh is treated
421  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
422  */
423 //================================================================================
424
425 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
426                                                    TIDSortedElemSet&       all0DElems )
427 {
428   SMDS_ElemIteratorPtr elemIt;
429   vector< const SMDS_MeshElement* > allNodes;
430   if ( elements.empty() )
431   {
432     allNodes.reserve( GetMeshDS()->NbNodes() );
433     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
434     while ( elemIt->more() )
435       allNodes.push_back( elemIt->next() );
436
437     elemIt = elemSetIterator( allNodes );
438   }
439   else
440   {
441     elemIt = elemSetIterator( elements );
442   }
443
444   while ( elemIt->more() )
445   {
446     const SMDS_MeshElement* e = elemIt->next();
447     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
448     while ( nodeIt->more() )
449     {
450       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
451       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
452       if ( it0D->more() )
453         all0DElems.insert( it0D->next() );
454       else {
455         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
456         all0DElems.insert( myLastCreatedElems.Last() );
457       }
458     }
459   }
460 }
461
462 //=======================================================================
463 //function : FindShape
464 //purpose  : Return an index of the shape theElem is on
465 //           or zero if a shape not found
466 //=======================================================================
467
468 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
469 {
470   myLastCreatedElems.Clear();
471   myLastCreatedNodes.Clear();
472
473   SMESHDS_Mesh * aMesh = GetMeshDS();
474   if ( aMesh->ShapeToMesh().IsNull() )
475     return 0;
476
477   int aShapeID = theElem->getshapeId();
478   if ( aShapeID < 1 )
479     return 0;
480
481   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
482     if ( sm->Contains( theElem ))
483       return aShapeID;
484
485   if ( theElem->GetType() == SMDSAbs_Node ) {
486     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
487   }
488   else {
489     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
490   }
491
492   TopoDS_Shape aShape; // the shape a node of theElem is on
493   if ( theElem->GetType() != SMDSAbs_Node )
494   {
495     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
496     while ( nodeIt->more() ) {
497       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
498       if ((aShapeID = node->getshapeId()) > 0) {
499         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
500           if ( sm->Contains( theElem ))
501             return aShapeID;
502           if ( aShape.IsNull() )
503             aShape = aMesh->IndexToShape( aShapeID );
504         }
505       }
506     }
507   }
508
509   // None of nodes is on a proper shape,
510   // find the shape among ancestors of aShape on which a node is
511   if ( !aShape.IsNull() ) {
512     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
513     for ( ; ancIt.More(); ancIt.Next() ) {
514       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
515       if ( sm && sm->Contains( theElem ))
516         return aMesh->ShapeToIndex( ancIt.Value() );
517     }
518   }
519   else
520   {
521     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
522     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
523     for ( ; id_sm != id2sm.end(); ++id_sm )
524       if ( id_sm->second->Contains( theElem ))
525         return id_sm->first;
526   }
527
528   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
529   return 0;
530 }
531
532 //=======================================================================
533 //function : IsMedium
534 //purpose  :
535 //=======================================================================
536
537 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
538                                 const SMDSAbs_ElementType typeToCheck)
539 {
540   bool isMedium = false;
541   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
542   while (it->more() && !isMedium ) {
543     const SMDS_MeshElement* elem = it->next();
544     isMedium = elem->IsMediumNode(node);
545   }
546   return isMedium;
547 }
548
549 //=======================================================================
550 //function : shiftNodesQuadTria
551 //purpose  : Shift nodes in the array corresponded to quadratic triangle
552 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
553 //=======================================================================
554
555 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
556 {
557   const SMDS_MeshNode* nd1 = aNodes[0];
558   aNodes[0] = aNodes[1];
559   aNodes[1] = aNodes[2];
560   aNodes[2] = nd1;
561   const SMDS_MeshNode* nd2 = aNodes[3];
562   aNodes[3] = aNodes[4];
563   aNodes[4] = aNodes[5];
564   aNodes[5] = nd2;
565 }
566
567 //=======================================================================
568 //function : nbEdgeConnectivity
569 //purpose  : return number of the edges connected with the theNode.
570 //           if theEdges has connections with the other type of the
571 //           elements, return -1
572 //=======================================================================
573
574 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
575 {
576   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
577   // int nb=0;
578   // while(elemIt->more()) {
579   //   elemIt->next();
580   //   nb++;
581   // }
582   // return nb;
583   return theNode->NbInverseElements();
584 }
585
586 //=======================================================================
587 //function : getNodesFromTwoTria
588 //purpose  : 
589 //=======================================================================
590
591 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
592                                 const SMDS_MeshElement * theTria2,
593                                 vector< const SMDS_MeshNode*>& N1,
594                                 vector< const SMDS_MeshNode*>& N2)
595 {
596   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
597   if ( N1.size() < 6 ) return false;
598   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
599   if ( N2.size() < 6 ) return false;
600
601   int sames[3] = {-1,-1,-1};
602   int nbsames = 0;
603   int i, j;
604   for(i=0; i<3; i++) {
605     for(j=0; j<3; j++) {
606       if(N1[i]==N2[j]) {
607         sames[i] = j;
608         nbsames++;
609         break;
610       }
611     }
612   }
613   if(nbsames!=2) return false;
614   if(sames[0]>-1) {
615     shiftNodesQuadTria(N1);
616     if(sames[1]>-1) {
617       shiftNodesQuadTria(N1);
618     }
619   }
620   i = sames[0] + sames[1] + sames[2];
621   for(; i<2; i++) {
622     shiftNodesQuadTria(N2);
623   }
624   // now we receive following N1 and N2 (using numeration as in the image below)
625   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
626   // i.e. first nodes from both arrays form a new diagonal
627   return true;
628 }
629
630 //=======================================================================
631 //function : InverseDiag
632 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
633 //           but having other common link.
634 //           Return False if args are improper
635 //=======================================================================
636
637 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
638                                     const SMDS_MeshElement * theTria2 )
639 {
640   MESSAGE("InverseDiag");
641   myLastCreatedElems.Clear();
642   myLastCreatedNodes.Clear();
643
644   if (!theTria1 || !theTria2)
645     return false;
646
647   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
648   if (!F1) return false;
649   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
650   if (!F2) return false;
651   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
652       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
653
654     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
655     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
656     //    |/ |                                         | \|
657     //  B +--+ 2                                     B +--+ 2
658
659     // put nodes in array and find out indices of the same ones
660     const SMDS_MeshNode* aNodes [6];
661     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
662     int i = 0;
663     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
664     while ( it->more() ) {
665       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
666
667       if ( i > 2 ) // theTria2
668         // find same node of theTria1
669         for ( int j = 0; j < 3; j++ )
670           if ( aNodes[ i ] == aNodes[ j ]) {
671             sameInd[ j ] = i;
672             sameInd[ i ] = j;
673             break;
674           }
675       // next
676       i++;
677       if ( i == 3 ) {
678         if ( it->more() )
679           return false; // theTria1 is not a triangle
680         it = theTria2->nodesIterator();
681       }
682       if ( i == 6 && it->more() )
683         return false; // theTria2 is not a triangle
684     }
685
686     // find indices of 1,2 and of A,B in theTria1
687     int iA = -1, iB = 0, i1 = 0, i2 = 0;
688     for ( i = 0; i < 6; i++ ) {
689       if ( sameInd [ i ] == -1 ) {
690         if ( i < 3 ) i1 = i;
691         else         i2 = i;
692       }
693       else if (i < 3) {
694         if ( iA >= 0) iB = i;
695         else          iA = i;
696       }
697     }
698     // nodes 1 and 2 should not be the same
699     if ( aNodes[ i1 ] == aNodes[ i2 ] )
700       return false;
701
702     // theTria1: A->2
703     aNodes[ iA ] = aNodes[ i2 ];
704     // theTria2: B->1
705     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
706
707     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
708     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
709
710     return true;
711
712   } // end if(F1 && F2)
713
714   // check case of quadratic faces
715   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
716       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
717     return false;
718   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
719       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
720     return false;
721
722   //       5
723   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
724   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
725   //    |   / |
726   //  7 +  +  + 6
727   //    | /9  |
728   //    |/    |
729   //  4 +--+--+ 3
730   //       8
731
732   vector< const SMDS_MeshNode* > N1;
733   vector< const SMDS_MeshNode* > N2;
734   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
735     return false;
736   // now we receive following N1 and N2 (using numeration as above image)
737   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
738   // i.e. first nodes from both arrays determ new diagonal
739
740   vector< const SMDS_MeshNode*> N1new( N1.size() );
741   vector< const SMDS_MeshNode*> N2new( N2.size() );
742   N1new.back() = N1.back(); // central node of biquadratic
743   N2new.back() = N2.back();
744   N1new[0] = N1[0];  N2new[0] = N1[0];
745   N1new[1] = N2[0];  N2new[1] = N1[1];
746   N1new[2] = N2[1];  N2new[2] = N2[0];
747   N1new[3] = N1[4];  N2new[3] = N1[3];
748   N1new[4] = N2[3];  N2new[4] = N2[5];
749   N1new[5] = N1[5];  N2new[5] = N1[4];
750   // change nodes in faces
751   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
752   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
753
754   // move the central node of biquadratic triangle
755   SMESH_MesherHelper helper( *GetMesh() );
756   for ( int is2nd = 0; is2nd < 2; ++is2nd )
757   {
758     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
759     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
760     if ( nodes.size() < 7 )
761       continue;
762     helper.SetSubShape( tria->getshapeId() );
763     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
764     gp_Pnt xyz;
765     if ( F.IsNull() )
766     {
767       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
768               SMESH_TNodeXYZ( nodes[4] ) +
769               SMESH_TNodeXYZ( nodes[5] )) / 3.;
770     }
771     else
772     {
773       bool checkUV;
774       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
775                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
776                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
777       TopLoc_Location loc;
778       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
779       xyz = S->Value( uv.X(), uv.Y() );
780       xyz.Transform( loc );
781       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
782            nodes[6]->getshapeId() > 0 )
783         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
784     }
785     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
786   }
787   return true;
788 }
789
790 //=======================================================================
791 //function : findTriangles
792 //purpose  : find triangles sharing theNode1-theNode2 link
793 //=======================================================================
794
795 static bool findTriangles(const SMDS_MeshNode *    theNode1,
796                           const SMDS_MeshNode *    theNode2,
797                           const SMDS_MeshElement*& theTria1,
798                           const SMDS_MeshElement*& theTria2)
799 {
800   if ( !theNode1 || !theNode2 ) return false;
801
802   theTria1 = theTria2 = 0;
803
804   set< const SMDS_MeshElement* > emap;
805   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
806   while (it->more()) {
807     const SMDS_MeshElement* elem = it->next();
808     if ( elem->NbCornerNodes() == 3 )
809       emap.insert( elem );
810   }
811   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
812   while (it->more()) {
813     const SMDS_MeshElement* elem = it->next();
814     if ( emap.count( elem )) {
815       if ( !theTria1 )
816       {
817         theTria1 = elem;
818       }
819       else  
820       {
821         theTria2 = elem;
822         // theTria1 must be element with minimum ID
823         if ( theTria2->GetID() < theTria1->GetID() )
824           std::swap( theTria2, theTria1 );
825         return true;
826       }
827     }
828   }
829   return false;
830 }
831
832 //=======================================================================
833 //function : InverseDiag
834 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
835 //           with ones built on the same 4 nodes but having other common link.
836 //           Return false if proper faces not found
837 //=======================================================================
838
839 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
840                                     const SMDS_MeshNode * theNode2)
841 {
842   myLastCreatedElems.Clear();
843   myLastCreatedNodes.Clear();
844
845   MESSAGE( "::InverseDiag()" );
846
847   const SMDS_MeshElement *tr1, *tr2;
848   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
849     return false;
850
851   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
852   if (!F1) return false;
853   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
854   if (!F2) return false;
855   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
856       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
857
858     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
859     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
860     //    |/ |                                    | \|
861     //  B +--+ 2                                B +--+ 2
862
863     // put nodes in array
864     // and find indices of 1,2 and of A in tr1 and of B in tr2
865     int i, iA1 = 0, i1 = 0;
866     const SMDS_MeshNode* aNodes1 [3];
867     SMDS_ElemIteratorPtr it;
868     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
869       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
870       if ( aNodes1[ i ] == theNode1 )
871         iA1 = i; // node A in tr1
872       else if ( aNodes1[ i ] != theNode2 )
873         i1 = i;  // node 1
874     }
875     int iB2 = 0, i2 = 0;
876     const SMDS_MeshNode* aNodes2 [3];
877     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
878       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
879       if ( aNodes2[ i ] == theNode2 )
880         iB2 = i; // node B in tr2
881       else if ( aNodes2[ i ] != theNode1 )
882         i2 = i;  // node 2
883     }
884
885     // nodes 1 and 2 should not be the same
886     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
887       return false;
888
889     // tr1: A->2
890     aNodes1[ iA1 ] = aNodes2[ i2 ];
891     // tr2: B->1
892     aNodes2[ iB2 ] = aNodes1[ i1 ];
893
894     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
895     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
896
897     return true;
898   }
899
900   // check case of quadratic faces
901   return InverseDiag(tr1,tr2);
902 }
903
904 //=======================================================================
905 //function : getQuadrangleNodes
906 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
907 //           fusion of triangles tr1 and tr2 having shared link on
908 //           theNode1 and theNode2
909 //=======================================================================
910
911 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
912                         const SMDS_MeshNode *    theNode1,
913                         const SMDS_MeshNode *    theNode2,
914                         const SMDS_MeshElement * tr1,
915                         const SMDS_MeshElement * tr2 )
916 {
917   if( tr1->NbNodes() != tr2->NbNodes() )
918     return false;
919   // find the 4-th node to insert into tr1
920   const SMDS_MeshNode* n4 = 0;
921   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
922   int i=0;
923   while ( !n4 && i<3 ) {
924     const SMDS_MeshNode * n = cast2Node( it->next() );
925     i++;
926     bool isDiag = ( n == theNode1 || n == theNode2 );
927     if ( !isDiag )
928       n4 = n;
929   }
930   // Make an array of nodes to be in a quadrangle
931   int iNode = 0, iFirstDiag = -1;
932   it = tr1->nodesIterator();
933   i=0;
934   while ( i<3 ) {
935     const SMDS_MeshNode * n = cast2Node( it->next() );
936     i++;
937     bool isDiag = ( n == theNode1 || n == theNode2 );
938     if ( isDiag ) {
939       if ( iFirstDiag < 0 )
940         iFirstDiag = iNode;
941       else if ( iNode - iFirstDiag == 1 )
942         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
943     }
944     else if ( n == n4 ) {
945       return false; // tr1 and tr2 should not have all the same nodes
946     }
947     theQuadNodes[ iNode++ ] = n;
948   }
949   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
950     theQuadNodes[ iNode ] = n4;
951
952   return true;
953 }
954
955 //=======================================================================
956 //function : DeleteDiag
957 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
958 //           with a quadrangle built on the same 4 nodes.
959 //           Return false if proper faces not found
960 //=======================================================================
961
962 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
963                                    const SMDS_MeshNode * theNode2)
964 {
965   myLastCreatedElems.Clear();
966   myLastCreatedNodes.Clear();
967
968   MESSAGE( "::DeleteDiag()" );
969
970   const SMDS_MeshElement *tr1, *tr2;
971   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
972     return false;
973
974   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
975   if (!F1) return false;
976   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
977   if (!F2) return false;
978   SMESHDS_Mesh * aMesh = GetMeshDS();
979
980   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
981       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
982
983     const SMDS_MeshNode* aNodes [ 4 ];
984     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
985       return false;
986
987     const SMDS_MeshElement* newElem = 0;
988     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
989     myLastCreatedElems.Append(newElem);
990     AddToSameGroups( newElem, tr1, aMesh );
991     int aShapeId = tr1->getshapeId();
992     if ( aShapeId )
993       {
994         aMesh->SetMeshElementOnShape( newElem, aShapeId );
995       }
996     aMesh->RemoveElement( tr1 );
997     aMesh->RemoveElement( tr2 );
998
999     return true;
1000   }
1001
1002   // check case of quadratic faces
1003   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1004     return false;
1005   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1006     return false;
1007
1008   //       5
1009   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1010   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1011   //    |   / |
1012   //  7 +  +  + 6
1013   //    | /9  |
1014   //    |/    |
1015   //  4 +--+--+ 3
1016   //       8
1017
1018   vector< const SMDS_MeshNode* > N1;
1019   vector< const SMDS_MeshNode* > N2;
1020   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1021     return false;
1022   // now we receive following N1 and N2 (using numeration as above image)
1023   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1024   // i.e. first nodes from both arrays determ new diagonal
1025
1026   const SMDS_MeshNode* aNodes[8];
1027   aNodes[0] = N1[0];
1028   aNodes[1] = N1[1];
1029   aNodes[2] = N2[0];
1030   aNodes[3] = N2[1];
1031   aNodes[4] = N1[3];
1032   aNodes[5] = N2[5];
1033   aNodes[6] = N2[3];
1034   aNodes[7] = N1[5];
1035
1036   const SMDS_MeshElement* newElem = 0;
1037   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1038                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1039   myLastCreatedElems.Append(newElem);
1040   AddToSameGroups( newElem, tr1, aMesh );
1041   int aShapeId = tr1->getshapeId();
1042   if ( aShapeId )
1043     {
1044       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045     }
1046   aMesh->RemoveElement( tr1 );
1047   aMesh->RemoveElement( tr2 );
1048
1049   // remove middle node (9)
1050   GetMeshDS()->RemoveNode( N1[4] );
1051
1052   return true;
1053 }
1054
1055 //=======================================================================
1056 //function : Reorient
1057 //purpose  : Reverse theElement orientation
1058 //=======================================================================
1059
1060 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1061 {
1062   MESSAGE("Reorient");
1063   myLastCreatedElems.Clear();
1064   myLastCreatedNodes.Clear();
1065
1066   if (!theElem)
1067     return false;
1068   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1069   if ( !it || !it->more() )
1070     return false;
1071
1072   const SMDSAbs_ElementType type = theElem->GetType();
1073   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1074     return false;
1075
1076   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1077   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1078   {
1079     const SMDS_VtkVolume* aPolyedre =
1080       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1081     if (!aPolyedre) {
1082       MESSAGE("Warning: bad volumic element");
1083       return false;
1084     }
1085     const int nbFaces = aPolyedre->NbFaces();
1086     vector<const SMDS_MeshNode *> poly_nodes;
1087     vector<int> quantities (nbFaces);
1088
1089     // reverse each face of the polyedre
1090     for (int iface = 1; iface <= nbFaces; iface++) {
1091       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1092       quantities[iface - 1] = nbFaceNodes;
1093
1094       for (inode = nbFaceNodes; inode >= 1; inode--) {
1095         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1096         poly_nodes.push_back(curNode);
1097       }
1098     }
1099     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100   }
1101   else // other elements
1102   {
1103     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1104     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1105     if ( interlace.empty() )
1106     {
1107       std::reverse( nodes.begin(), nodes.end() ); // polygon
1108     }
1109     else if ( interlace.size() > 1 )
1110     {
1111       SMDS_MeshCell::applyInterlace( interlace, nodes );
1112     }
1113     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1114   }
1115   return false;
1116 }
1117
1118 //================================================================================
1119 /*!
1120  * \brief Reorient faces.
1121  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1122  * \param theDirection - desired direction of normal of \a theFace
1123  * \param theFace - one of \a theFaces that sould be oriented according to
1124  *        \a theDirection and whose orientation defines orientation of other faces
1125  * \return number of reoriented faces.
1126  */
1127 //================================================================================
1128
1129 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1130                                   const gp_Dir&            theDirection,
1131                                   const SMDS_MeshElement * theFace)
1132 {
1133   int nbReori = 0;
1134   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1135
1136   if ( theFaces.empty() )
1137   {
1138     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1139     while ( fIt->more() )
1140       theFaces.insert( theFaces.end(), fIt->next() );
1141   }
1142
1143   // orient theFace according to theDirection
1144   gp_XYZ normal;
1145   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1146   if ( normal * theDirection.XYZ() < 0 )
1147     nbReori += Reorient( theFace );
1148
1149   // Orient other faces
1150
1151   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1152   TIDSortedElemSet avoidSet;
1153   set< SMESH_TLink > checkedLinks;
1154   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1155
1156   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1157     theFaces.erase( theFace );
1158   startFaces.insert( theFace );
1159
1160   int nodeInd1, nodeInd2;
1161   const SMDS_MeshElement*           otherFace;
1162   vector< const SMDS_MeshElement* > facesNearLink;
1163   vector< std::pair< int, int > >   nodeIndsOfFace;
1164
1165   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1166   while ( !startFaces.empty() )
1167   {
1168     startFace = startFaces.begin();
1169     theFace = *startFace;
1170     startFaces.erase( startFace );
1171     if ( !visitedFaces.insert( theFace ).second )
1172       continue;
1173
1174     avoidSet.clear();
1175     avoidSet.insert(theFace);
1176
1177     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1178
1179     const int nbNodes = theFace->NbCornerNodes();
1180     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1181     {
1182       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1183       linkIt_isNew = checkedLinks.insert( link );
1184       if ( !linkIt_isNew.second )
1185       {
1186         // link has already been checked and won't be encountered more
1187         // if the group (theFaces) is manifold
1188         //checkedLinks.erase( linkIt_isNew.first );
1189       }
1190       else
1191       {
1192         facesNearLink.clear();
1193         nodeIndsOfFace.clear();
1194         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1195                                                              theFaces, avoidSet,
1196                                                              &nodeInd1, &nodeInd2 )))
1197           if ( otherFace != theFace)
1198           {
1199             facesNearLink.push_back( otherFace );
1200             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1201             avoidSet.insert( otherFace );
1202           }
1203         if ( facesNearLink.size() > 1 )
1204         {
1205           // NON-MANIFOLD mesh shell !
1206           // select a face most co-directed with theFace,
1207           // other faces won't be visited this time
1208           gp_XYZ NF, NOF;
1209           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1210           double proj, maxProj = -1;
1211           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1212             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1213             if (( proj = Abs( NF * NOF )) > maxProj ) {
1214               maxProj = proj;
1215               otherFace = facesNearLink[i];
1216               nodeInd1  = nodeIndsOfFace[i].first;
1217               nodeInd2  = nodeIndsOfFace[i].second;
1218             }
1219           }
1220           // not to visit rejected faces
1221           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1222             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1223               visitedFaces.insert( facesNearLink[i] );
1224         }
1225         else if ( facesNearLink.size() == 1 )
1226         {
1227           otherFace = facesNearLink[0];
1228           nodeInd1  = nodeIndsOfFace.back().first;
1229           nodeInd2  = nodeIndsOfFace.back().second;
1230         }
1231         if ( otherFace && otherFace != theFace)
1232         {
1233           // link must be reverse in otherFace if orientation ot otherFace
1234           // is same as that of theFace
1235           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1236           {
1237             nbReori += Reorient( otherFace );
1238           }
1239           startFaces.insert( otherFace );
1240         }
1241       }
1242       std::swap( link.first, link.second ); // reverse the link
1243     }
1244   }
1245   return nbReori;
1246 }
1247
1248 //=======================================================================
1249 //function : getBadRate
1250 //purpose  :
1251 //=======================================================================
1252
1253 static double getBadRate (const SMDS_MeshElement*               theElem,
1254                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1255 {
1256   SMESH::Controls::TSequenceOfXYZ P;
1257   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1258     return 1e100;
1259   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1260   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1261 }
1262
1263 //=======================================================================
1264 //function : QuadToTri
1265 //purpose  : Cut quadrangles into triangles.
1266 //           theCrit is used to select a diagonal to cut
1267 //=======================================================================
1268
1269 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1270                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1271 {
1272   myLastCreatedElems.Clear();
1273   myLastCreatedNodes.Clear();
1274
1275   if ( !theCrit.get() )
1276     return false;
1277
1278   SMESHDS_Mesh * aMesh = GetMeshDS();
1279
1280   Handle(Geom_Surface) surface;
1281   SMESH_MesherHelper   helper( *GetMesh() );
1282
1283   TIDSortedElemSet::iterator itElem;
1284   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1285   {
1286     const SMDS_MeshElement* elem = *itElem;
1287     if ( !elem || elem->GetType() != SMDSAbs_Face )
1288       continue;
1289     if ( elem->NbCornerNodes() != 4 )
1290       continue;
1291
1292     // retrieve element nodes
1293     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1294
1295     // compare two sets of possible triangles
1296     double aBadRate1, aBadRate2; // to what extent a set is bad
1297     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1298     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1299     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1300
1301     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1302     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1303     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1304
1305     const int aShapeId = FindShape( elem );
1306     const SMDS_MeshElement* newElem1 = 0;
1307     const SMDS_MeshElement* newElem2 = 0;
1308
1309     if ( !elem->IsQuadratic() ) // split liner quadrangle
1310     {
1311       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1312       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1313       if ( aBadRate1 <= aBadRate2 ) {
1314         // tr1 + tr2 is better
1315         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1316         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1317       }
1318       else {
1319         // tr3 + tr4 is better
1320         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1321         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1322       }
1323     }
1324     else // split quadratic quadrangle
1325     {
1326       helper.SetIsQuadratic( true );
1327       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1328
1329       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1330       if ( aNodes.size() == 9 )
1331       {
1332         helper.SetIsBiQuadratic( true );
1333         if ( aBadRate1 <= aBadRate2 )
1334           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1335         else
1336           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1337       }
1338       // create a new element
1339       if ( aBadRate1 <= aBadRate2 ) {
1340         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1341         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1342       }
1343       else {
1344         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1345         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1346       }
1347     } // quadratic case
1348
1349     // care of a new element
1350
1351     myLastCreatedElems.Append(newElem1);
1352     myLastCreatedElems.Append(newElem2);
1353     AddToSameGroups( newElem1, elem, aMesh );
1354     AddToSameGroups( newElem2, elem, aMesh );
1355
1356     // put a new triangle on the same shape
1357     if ( aShapeId )
1358       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1359     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1360
1361     aMesh->RemoveElement( elem );
1362   }
1363   return true;
1364 }
1365
1366 //=======================================================================
1367 /*!
1368  * \brief Split each of given quadrangles into 4 triangles.
1369  * \param theElems - The faces to be splitted. If empty all faces are split.
1370  */
1371 //=======================================================================
1372
1373 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1374 {
1375   myLastCreatedElems.Clear();
1376   myLastCreatedNodes.Clear();
1377
1378   SMESH_MesherHelper helper( *GetMesh() );
1379   helper.SetElementsOnShape( true );
1380
1381   SMDS_ElemIteratorPtr faceIt;
1382   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1383   else                    faceIt = elemSetIterator( theElems );
1384
1385   bool   checkUV;
1386   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1387   gp_XYZ xyz[9];
1388   vector< const SMDS_MeshNode* > nodes;
1389   SMESHDS_SubMesh*               subMeshDS;
1390   TopoDS_Face                    F;
1391   Handle(Geom_Surface)           surface;
1392   TopLoc_Location                loc;
1393
1394   while ( faceIt->more() )
1395   {
1396     const SMDS_MeshElement* quad = faceIt->next();
1397     if ( !quad || quad->NbCornerNodes() != 4 )
1398       continue;
1399
1400     // get a surface the quad is on
1401
1402     if ( quad->getshapeId() < 1 )
1403     {
1404       F.Nullify();
1405       helper.SetSubShape( 0 );
1406       subMeshDS = 0;
1407     }
1408     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1409     {
1410       helper.SetSubShape( quad->getshapeId() );
1411       if ( !helper.GetSubShape().IsNull() &&
1412            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1413       {
1414         F = TopoDS::Face( helper.GetSubShape() );
1415         surface = BRep_Tool::Surface( F, loc );
1416         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1417       }
1418       else
1419       {
1420         helper.SetSubShape( 0 );
1421         subMeshDS = 0;
1422       }
1423     }
1424
1425     // create a central node
1426
1427     const SMDS_MeshNode* nCentral;
1428     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1429
1430     if ( nodes.size() == 9 )
1431     {
1432       nCentral = nodes.back();
1433     }
1434     else
1435     {
1436       size_t iN = 0;
1437       if ( F.IsNull() )
1438       {
1439         for ( ; iN < nodes.size(); ++iN )
1440           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1441
1442         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1443           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1444
1445         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1446                                    xyz[0], xyz[1], xyz[2], xyz[3],
1447                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1448       }
1449       else
1450       {
1451         for ( ; iN < nodes.size(); ++iN )
1452           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1453
1454         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1455           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1456
1457         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1458                                   uv[0], uv[1], uv[2], uv[3],
1459                                   uv[4], uv[5], uv[6], uv[7] );
1460
1461         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1462         xyz[ 8 ] = p.XYZ();
1463       }
1464
1465       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1466                                  uv[8].X(), uv[8].Y() );
1467       myLastCreatedNodes.Append( nCentral );
1468     }
1469
1470     // create 4 triangles
1471
1472     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1473     
1474     helper.SetIsQuadratic  ( nodes.size() > 4 );
1475     helper.SetIsBiQuadratic( nodes.size() == 9 );
1476     if ( helper.GetIsQuadratic() )
1477       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1478
1479     for ( int i = 0; i < 4; ++i )
1480     {
1481       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1482                                                nodes[(i+1)%4],
1483                                                nCentral );
1484       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1485       myLastCreatedElems.Append( tria );
1486     }
1487   }
1488 }
1489
1490 //=======================================================================
1491 //function : BestSplit
1492 //purpose  : Find better diagonal for cutting.
1493 //=======================================================================
1494
1495 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1496                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1497 {
1498   myLastCreatedElems.Clear();
1499   myLastCreatedNodes.Clear();
1500
1501   if (!theCrit.get())
1502     return -1;
1503
1504   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1505     return -1;
1506
1507   if( theQuad->NbNodes()==4 ||
1508       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1509
1510     // retrieve element nodes
1511     const SMDS_MeshNode* aNodes [4];
1512     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1513     int i = 0;
1514     //while (itN->more())
1515     while (i<4) {
1516       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1517     }
1518     // compare two sets of possible triangles
1519     double aBadRate1, aBadRate2; // to what extent a set is bad
1520     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1521     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1522     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1523
1524     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1525     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1526     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1527     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1528     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1529     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1530       return 1; // diagonal 1-3
1531
1532     return 2; // diagonal 2-4
1533   }
1534   return -1;
1535 }
1536
1537 namespace
1538 {
1539   // Methods of splitting volumes into tetra
1540
1541   const int theHexTo5_1[5*4+1] =
1542     {
1543       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1544     };
1545   const int theHexTo5_2[5*4+1] =
1546     {
1547       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1548     };
1549   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1550
1551   const int theHexTo6_1[6*4+1] =
1552     {
1553       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
1554     };
1555   const int theHexTo6_2[6*4+1] =
1556     {
1557       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
1558     };
1559   const int theHexTo6_3[6*4+1] =
1560     {
1561       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
1562     };
1563   const int theHexTo6_4[6*4+1] =
1564     {
1565       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
1566     };
1567   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1568
1569   const int thePyraTo2_1[2*4+1] =
1570     {
1571       0, 1, 2, 4,    0, 2, 3, 4,   -1
1572     };
1573   const int thePyraTo2_2[2*4+1] =
1574     {
1575       1, 2, 3, 4,    1, 3, 0, 4,   -1
1576     };
1577   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1578
1579   const int thePentaTo3_1[3*4+1] =
1580     {
1581       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1582     };
1583   const int thePentaTo3_2[3*4+1] =
1584     {
1585       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1586     };
1587   const int thePentaTo3_3[3*4+1] =
1588     {
1589       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1590     };
1591   const int thePentaTo3_4[3*4+1] =
1592     {
1593       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1594     };
1595   const int thePentaTo3_5[3*4+1] =
1596     {
1597       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1598     };
1599   const int thePentaTo3_6[3*4+1] =
1600     {
1601       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1602     };
1603   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1604                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1605
1606   // Methods of splitting hexahedron into prisms
1607
1608   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1609     {
1610       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1611     };
1612   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1613     {
1614       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1615     };
1616   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1617     {
1618       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1619     };
1620
1621   const int theHexTo2Prisms_BT_1[6*2+1] =
1622     {
1623       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1624     };
1625   const int theHexTo2Prisms_BT_2[6*2+1] =
1626     {
1627       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1628     };
1629   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1630
1631   const int theHexTo2Prisms_LR_1[6*2+1] =
1632     {
1633       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1634     };
1635   const int theHexTo2Prisms_LR_2[6*2+1] =
1636     {
1637       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1638     };
1639   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1640
1641   const int theHexTo2Prisms_FB_1[6*2+1] =
1642     {
1643       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1644     };
1645   const int theHexTo2Prisms_FB_2[6*2+1] =
1646     {
1647       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1648     };
1649   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1650
1651
1652   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1653   {
1654     int _n1, _n2, _n3;
1655     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1656     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1657     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1658                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1659   };
1660   struct TSplitMethod
1661   {
1662     int        _nbSplits;
1663     int        _nbCorners;
1664     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1665     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1666     bool       _ownConn;      //!< to delete _connectivity in destructor
1667     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1668
1669     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1670       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1671     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1672     bool hasFacet( const TTriangleFacet& facet ) const
1673     {
1674       if ( _nbCorners == 4 )
1675       {
1676         const int* tetConn = _connectivity;
1677         for ( ; tetConn[0] >= 0; tetConn += 4 )
1678           if (( facet.contains( tetConn[0] ) +
1679                 facet.contains( tetConn[1] ) +
1680                 facet.contains( tetConn[2] ) +
1681                 facet.contains( tetConn[3] )) == 3 )
1682             return true;
1683       }
1684       else // prism, _nbCorners == 6
1685       {
1686         const int* prismConn = _connectivity;
1687         for ( ; prismConn[0] >= 0; prismConn += 6 )
1688         {
1689           if (( facet.contains( prismConn[0] ) &&
1690                 facet.contains( prismConn[1] ) &&
1691                 facet.contains( prismConn[2] ))
1692               ||
1693               ( facet.contains( prismConn[3] ) &&
1694                 facet.contains( prismConn[4] ) &&
1695                 facet.contains( prismConn[5] )))
1696             return true;
1697         }
1698       }
1699       return false;
1700     }
1701   };
1702
1703   //=======================================================================
1704   /*!
1705    * \brief return TSplitMethod for the given element to split into tetrahedra
1706    */
1707   //=======================================================================
1708
1709   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1710   {
1711     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1712
1713     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1714     // an edge and a face barycenter; tertaherdons are based on triangles and
1715     // a volume barycenter
1716     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1717
1718     // Find out how adjacent volumes are split
1719
1720     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1721     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1722     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1723     {
1724       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1725       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1726       if ( nbNodes < 4 ) continue;
1727
1728       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1729       const int* nInd = vol.GetFaceNodesIndices( iF );
1730       if ( nbNodes == 4 )
1731       {
1732         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1733         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1734         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1735         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1736       }
1737       else
1738       {
1739         int iCom = 0; // common node of triangle faces to split into
1740         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1741         {
1742           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1743                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1744                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1745           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1746                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1747                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1748           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1749           {
1750             triaSplits.push_back( t012 );
1751             triaSplits.push_back( t023 );
1752             break;
1753           }
1754         }
1755       }
1756       if ( !triaSplits.empty() )
1757         hasAdjacentSplits = true;
1758     }
1759
1760     // Among variants of split method select one compliant with adjacent volumes
1761
1762     TSplitMethod method;
1763     if ( !vol.Element()->IsPoly() && !is24TetMode )
1764     {
1765       int nbVariants = 2, nbTet = 0;
1766       const int** connVariants = 0;
1767       switch ( vol.Element()->GetEntityType() )
1768       {
1769       case SMDSEntity_Hexa:
1770       case SMDSEntity_Quad_Hexa:
1771       case SMDSEntity_TriQuad_Hexa:
1772         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1773           connVariants = theHexTo5, nbTet = 5;
1774         else
1775           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1776         break;
1777       case SMDSEntity_Pyramid:
1778       case SMDSEntity_Quad_Pyramid:
1779         connVariants = thePyraTo2;  nbTet = 2;
1780         break;
1781       case SMDSEntity_Penta:
1782       case SMDSEntity_Quad_Penta:
1783         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1784         break;
1785       default:
1786         nbVariants = 0;
1787       }
1788       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1789       {
1790         // check method compliancy with adjacent tetras,
1791         // all found splits must be among facets of tetras described by this method
1792         method = TSplitMethod( nbTet, connVariants[variant] );
1793         if ( hasAdjacentSplits && method._nbSplits > 0 )
1794         {
1795           bool facetCreated = true;
1796           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1797           {
1798             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1799             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1800               facetCreated = method.hasFacet( *facet );
1801           }
1802           if ( !facetCreated )
1803             method = TSplitMethod(0); // incompatible method
1804         }
1805       }
1806     }
1807     if ( method._nbSplits < 1 )
1808     {
1809       // No standard method is applicable, use a generic solution:
1810       // each facet of a volume is split into triangles and
1811       // each of triangles and a volume barycenter form a tetrahedron.
1812
1813       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1814
1815       int* connectivity = new int[ maxTetConnSize + 1 ];
1816       method._connectivity = connectivity;
1817       method._ownConn = true;
1818       method._baryNode = !isHex27; // to create central node or not
1819
1820       int connSize = 0;
1821       int baryCenInd = vol.NbNodes() - int( isHex27 );
1822       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1823       {
1824         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1825         const int*   nInd = vol.GetFaceNodesIndices( iF );
1826         // find common node of triangle facets of tetra to create
1827         int iCommon = 0; // index in linear numeration
1828         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1829         if ( !triaSplits.empty() )
1830         {
1831           // by found facets
1832           const TTriangleFacet* facet = &triaSplits.front();
1833           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1834             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1835                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1836               break;
1837         }
1838         else if ( nbNodes > 3 && !is24TetMode )
1839         {
1840           // find the best method of splitting into triangles by aspect ratio
1841           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1842           map< double, int > badness2iCommon;
1843           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1844           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1845           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1846           {
1847             double badness = 0;
1848             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1849             {
1850               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1851                                       nodes[ iQ*((iLast-1)%nbNodes)],
1852                                       nodes[ iQ*((iLast  )%nbNodes)]);
1853               badness += getBadRate( &tria, aspectRatio );
1854             }
1855             badness2iCommon.insert( make_pair( badness, iCommon ));
1856           }
1857           // use iCommon with lowest badness
1858           iCommon = badness2iCommon.begin()->second;
1859         }
1860         if ( iCommon >= nbNodes )
1861           iCommon = 0; // something wrong
1862
1863         // fill connectivity of tetrahedra based on a current face
1864         int nbTet = nbNodes - 2;
1865         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1866         {
1867           int faceBaryCenInd;
1868           if ( isHex27 )
1869           {
1870             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1871             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1872           }
1873           else
1874           {
1875             method._faceBaryNode[ iF ] = 0;
1876             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1877           }
1878           nbTet = nbNodes;
1879           for ( int i = 0; i < nbTet; ++i )
1880           {
1881             int i1 = i, i2 = (i+1) % nbNodes;
1882             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1883             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1884             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1885             connectivity[ connSize++ ] = faceBaryCenInd;
1886             connectivity[ connSize++ ] = baryCenInd;
1887           }
1888         }
1889         else
1890         {
1891           for ( int i = 0; i < nbTet; ++i )
1892           {
1893             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1894             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1895             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1896             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1897             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1898             connectivity[ connSize++ ] = baryCenInd;
1899           }
1900         }
1901         method._nbSplits += nbTet;
1902
1903       } // loop on volume faces
1904
1905       connectivity[ connSize++ ] = -1;
1906
1907     } // end of generic solution
1908
1909     return method;
1910   }
1911   //=======================================================================
1912   /*!
1913    * \brief return TSplitMethod to split haxhedron into prisms
1914    */
1915   //=======================================================================
1916
1917   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
1918                                     const int        methodFlags,
1919                                     const int        facetToSplit)
1920   {
1921     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
1922     // B, T, L, B, R, F
1923     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
1924
1925     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
1926     {
1927       static TSplitMethod to4methods[4]; // order BT, LR, FB
1928       if ( to4methods[iF]._nbSplits == 0 )
1929       {
1930         switch ( iF ) {
1931         case 0:
1932           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
1933           to4methods[iF]._faceBaryNode[ 0 ] = 0;
1934           to4methods[iF]._faceBaryNode[ 1 ] = 0;
1935           break;
1936         case 1:
1937           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
1938           to4methods[iF]._faceBaryNode[ 2 ] = 0;
1939           to4methods[iF]._faceBaryNode[ 4 ] = 0;
1940           break;
1941         case 2:
1942           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
1943           to4methods[iF]._faceBaryNode[ 3 ] = 0;
1944           to4methods[iF]._faceBaryNode[ 5 ] = 0;
1945           break;
1946         default: return to4methods[3];
1947         }
1948         to4methods[iF]._nbSplits  = 4;
1949         to4methods[iF]._nbCorners = 6;
1950       }
1951       return to4methods[iF];
1952     }
1953     // else if ( methodFlags == HEXA_TO_2_PRISMS )
1954
1955     TSplitMethod method;
1956
1957     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1958
1959     const int nbVariants = 2, nbSplits = 2;
1960     const int** connVariants = 0;
1961     switch ( iF ) {
1962     case 0: connVariants = theHexTo2Prisms_BT; break;
1963     case 1: connVariants = theHexTo2Prisms_LR; break;
1964     case 2: connVariants = theHexTo2Prisms_FB; break;
1965     default: return method;
1966     }
1967
1968     // look for prisms adjacent via facetToSplit and an opposite one
1969     for ( int is2nd = 0; is2nd < 2; ++is2nd )
1970     {
1971       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
1972       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
1973       if ( nbNodes != 4 ) return method;
1974
1975       const int* nInd = vol.GetFaceNodesIndices( iFacet );
1976       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1977       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1978       TTriangleFacet* t;
1979       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1980         t = &t012;
1981       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1982         t = &t123;
1983       else
1984         continue;
1985
1986       // there are adjacent prism
1987       for ( int variant = 0; variant < nbVariants; ++variant )
1988       {
1989         // check method compliancy with adjacent prisms,
1990         // the found prism facets must be among facets of prisms described by current method
1991         method._nbSplits     = nbSplits;
1992         method._nbCorners    = 6;
1993         method._connectivity = connVariants[ variant ];
1994         if ( method.hasFacet( *t ))
1995           return method;
1996       }
1997     }
1998
1999     // No adjacent prisms. Select a variant with a best aspect ratio.
2000
2001     double badness[2] = { 0, 0 };
2002     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2003     const SMDS_MeshNode** nodes = vol.GetNodes();
2004     for ( int variant = 0; variant < nbVariants; ++variant )
2005       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2006       {
2007         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2008         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2009
2010         method._connectivity = connVariants[ variant ];
2011         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2012         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2013         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2014
2015         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2016                                 nodes[ t->_n2 ],
2017                                 nodes[ t->_n3 ] );
2018         badness[ variant ] += getBadRate( &tria, aspectRatio );
2019       }
2020     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2021
2022     method._nbSplits     = nbSplits;
2023     method._nbCorners    = 6;
2024     method._connectivity = connVariants[ iBetter ];
2025
2026     return method;
2027   }
2028
2029   //================================================================================
2030   /*!
2031    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2032    */
2033   //================================================================================
2034
2035   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2036                                        const SMDSAbs_GeometryType geom ) const
2037   {
2038     // find the tetrahedron including the three nodes of facet
2039     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2040     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2041     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2042     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2043     while ( volIt1->more() )
2044     {
2045       const SMDS_MeshElement* v = volIt1->next();
2046       if ( v->GetGeomType() != geom )
2047         continue;
2048       const int lastCornerInd = v->NbCornerNodes() - 1;
2049       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2050         continue; // medium node not allowed
2051       const int ind2 = v->GetNodeIndex( n2 );
2052       if ( ind2 < 0 || lastCornerInd < ind2 )
2053         continue;
2054       const int ind3 = v->GetNodeIndex( n3 );
2055       if ( ind3 < 0 || lastCornerInd < ind3 )
2056         continue;
2057       return true;
2058     }
2059     return false;
2060   }
2061
2062   //=======================================================================
2063   /*!
2064    * \brief A key of a face of volume
2065    */
2066   //=======================================================================
2067
2068   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2069   {
2070     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2071     {
2072       TIDSortedNodeSet sortedNodes;
2073       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2074       int nbNodes = vol.NbFaceNodes( iF );
2075       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2076       for ( int i = 0; i < nbNodes; i += iQ )
2077         sortedNodes.insert( fNodes[i] );
2078       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2079       first.first   = (*(n++))->GetID();
2080       first.second  = (*(n++))->GetID();
2081       second.first  = (*(n++))->GetID();
2082       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2083     }
2084   };
2085 } // namespace
2086
2087 //=======================================================================
2088 //function : SplitVolumes
2089 //purpose  : Split volume elements into tetrahedra or prisms.
2090 //           If facet ID < 0, element is split into tetrahedra,
2091 //           else a hexahedron is split into prisms so that the given facet is
2092 //           split into triangles
2093 //=======================================================================
2094
2095 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2096                                      const int            theMethodFlags)
2097 {
2098   // std-like iterator on coordinates of nodes of mesh element
2099   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2100   NXyzIterator xyzEnd;
2101
2102   SMDS_VolumeTool    volTool;
2103   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2104   fHelper.ToFixNodeParameters( true );
2105
2106   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2107   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2108
2109   SMESH_SequenceOfElemPtr newNodes, newElems;
2110
2111   // map face of volume to it's baricenrtic node
2112   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2113   double bc[3];
2114
2115   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2116   for ( ; elem2facet != theElems.end(); ++elem2facet )
2117   {
2118     const SMDS_MeshElement* elem = elem2facet->first;
2119     const int       facetToSplit = elem2facet->second;
2120     if ( elem->GetType() != SMDSAbs_Volume )
2121       continue;
2122     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2123     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2124       continue;
2125
2126     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2127
2128     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2129                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2130                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2131     if ( splitMethod._nbSplits < 1 ) continue;
2132
2133     // find submesh to add new tetras to
2134     if ( !subMesh || !subMesh->Contains( elem ))
2135     {
2136       int shapeID = FindShape( elem );
2137       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2138       subMesh = GetMeshDS()->MeshElements( shapeID );
2139     }
2140     int iQ;
2141     if ( elem->IsQuadratic() )
2142     {
2143       iQ = 2;
2144       // add quadratic links to the helper
2145       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2146       {
2147         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2148         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2149         for ( int iN = 0; iN < nbN; iN += iQ )
2150           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2151       }
2152       helper.SetIsQuadratic( true );
2153     }
2154     else
2155     {
2156       iQ = 1;
2157       helper.SetIsQuadratic( false );
2158     }
2159     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2160                                         volTool.GetNodes() + elem->NbNodes() );
2161     helper.SetElementsOnShape( true );
2162     if ( splitMethod._baryNode )
2163     {
2164       // make a node at barycenter
2165       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2166       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2167       nodes.push_back( gcNode );
2168       newNodes.Append( gcNode );
2169     }
2170     if ( !splitMethod._faceBaryNode.empty() )
2171     {
2172       // make or find baricentric nodes of faces
2173       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2174       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2175       {
2176         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2177           volFace2BaryNode.insert
2178           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2179         if ( !f_n->second )
2180         {
2181           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2182           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2183         }
2184         nodes.push_back( iF_n->second = f_n->second );
2185       }
2186     }
2187
2188     // make new volumes
2189     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2190     const int* volConn = splitMethod._connectivity;
2191     if ( splitMethod._nbCorners == 4 ) // tetra
2192       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2193         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2194                                                             nodes[ volConn[1] ],
2195                                                             nodes[ volConn[2] ],
2196                                                             nodes[ volConn[3] ]));
2197     else // prisms
2198       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2199         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2200                                                             nodes[ volConn[1] ],
2201                                                             nodes[ volConn[2] ],
2202                                                             nodes[ volConn[3] ],
2203                                                             nodes[ volConn[4] ],
2204                                                             nodes[ volConn[5] ]));
2205
2206     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2207
2208     // Split faces on sides of the split volume
2209
2210     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2211     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2212     {
2213       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2214       if ( nbNodes < 4 ) continue;
2215
2216       // find an existing face
2217       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2218                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2219       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2220                                                                        /*noMedium=*/false))
2221       {
2222         // make triangles
2223         helper.SetElementsOnShape( false );
2224         vector< const SMDS_MeshElement* > triangles;
2225
2226         // find submesh to add new triangles in
2227         if ( !fSubMesh || !fSubMesh->Contains( face ))
2228         {
2229           int shapeID = FindShape( face );
2230           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2231         }
2232         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2233         if ( iF_n != splitMethod._faceBaryNode.end() )
2234         {
2235           const SMDS_MeshNode *baryNode = iF_n->second;
2236           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2237           {
2238             const SMDS_MeshNode* n1 = fNodes[iN];
2239             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2240             const SMDS_MeshNode *n3 = baryNode;
2241             if ( !volTool.IsFaceExternal( iF ))
2242               swap( n2, n3 );
2243             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2244           }
2245           if ( fSubMesh ) // update position of the bary node on geometry
2246           {
2247             if ( subMesh )
2248               subMesh->RemoveNode( baryNode, false );
2249             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2250             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2251             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2252             {
2253               fHelper.SetSubShape( s );
2254               gp_XY uv( 1e100, 1e100 );
2255               double distXYZ[4];
2256               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2257                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2258                    uv.X() < 1e100 )
2259               {
2260                 // node is too far from the surface
2261                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2262                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2263                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2264               }
2265             }
2266           }
2267         }
2268         else
2269         {
2270           // among possible triangles create ones discribed by split method
2271           const int* nInd = volTool.GetFaceNodesIndices( iF );
2272           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2273           int iCom = 0; // common node of triangle faces to split into
2274           list< TTriangleFacet > facets;
2275           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2276           {
2277             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2278                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2279                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2280             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2281                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2282                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2283             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2284             {
2285               facets.push_back( t012 );
2286               facets.push_back( t023 );
2287               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2288                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2289                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2290                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2291               break;
2292             }
2293           }
2294           list< TTriangleFacet >::iterator facet = facets.begin();
2295           if ( facet == facets.end() )
2296             break;
2297           for ( ; facet != facets.end(); ++facet )
2298           {
2299             if ( !volTool.IsFaceExternal( iF ))
2300               swap( facet->_n2, facet->_n3 );
2301             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2302                                                  volNodes[ facet->_n2 ],
2303                                                  volNodes[ facet->_n3 ]));
2304           }
2305         }
2306         for ( int i = 0; i < triangles.size(); ++i )
2307         {
2308           if ( !triangles[i] ) continue;
2309           if ( fSubMesh )
2310             fSubMesh->AddElement( triangles[i]);
2311           newElems.Append( triangles[i] );
2312         }
2313         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2314         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2315
2316       } // while a face based on facet nodes exists
2317     } // loop on volume faces to split them into triangles
2318
2319     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2320
2321     if ( geomType == SMDSEntity_TriQuad_Hexa )
2322     {
2323       // remove medium nodes that could become free
2324       for ( int i = 20; i < volTool.NbNodes(); ++i )
2325         if ( volNodes[i]->NbInverseElements() == 0 )
2326           GetMeshDS()->RemoveNode( volNodes[i] );
2327     }
2328   } // loop on volumes to split
2329   
2330   myLastCreatedNodes = newNodes;
2331   myLastCreatedElems = newElems;
2332 }
2333
2334 //=======================================================================
2335 //function : GetHexaFacetsToSplit
2336 //purpose  : For hexahedra that will be split into prisms, finds facets to
2337 //           split into triangles. Only hexahedra adjacent to the one closest
2338 //           to theFacetNormal.Location() are returned.
2339 //param [in,out] theHexas - the hexahedra
2340 //param [in]     theFacetNormal - facet normal
2341 //param [out]    theFacets - the hexahedra and found facet IDs
2342 //=======================================================================
2343
2344 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2345                                              const gp_Ax1&     theFacetNormal,
2346                                              TFacetOfElem &    theFacets)
2347 {
2348   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2349
2350   // Find a hexa closest to the location of theFacetNormal
2351
2352   const SMDS_MeshElement* startHex;
2353   {
2354     // get SMDS_ElemIteratorPtr on theHexas
2355     typedef const SMDS_MeshElement*                                      TValue;
2356     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2357     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2358     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2359     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2360     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2361       ( new TElemSetIter( theHexas.begin(),
2362                           theHexas.end(),
2363                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2364
2365     SMESH_ElementSearcher* searcher =
2366       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2367
2368     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2369
2370     delete searcher;
2371
2372     if ( !startHex )
2373       throw SALOME_Exception( THIS_METHOD "startHex not found");
2374   }
2375
2376   // Select a facet of startHex by theFacetNormal
2377
2378   SMDS_VolumeTool vTool( startHex );
2379   double norm[3], dot, maxDot = 0;
2380   int facetID = -1;
2381   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2382     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2383     {
2384       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2385       if ( dot > maxDot )
2386       {
2387         facetID = iF;
2388         maxDot = dot;
2389       }
2390     }
2391   if ( facetID < 0 )
2392     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2393
2394   // Fill theFacets starting from facetID of startHex
2395
2396   // facets used for seach of volumes adjacent to already treated ones
2397   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2398   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2399   TFacetMap facetsToCheck;
2400
2401   set<const SMDS_MeshNode*> facetNodes;
2402   const SMDS_MeshElement*   curHex;
2403
2404   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2405
2406   while ( startHex )
2407   {
2408     // move in two directions from startHex via facetID
2409     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2410     {
2411       curHex       = startHex;
2412       int curFacet = facetID;
2413       if ( is2nd ) // do not treat startHex twice
2414       {
2415         vTool.Set( curHex );
2416         if ( vTool.IsFreeFace( curFacet, &curHex ))
2417         {
2418           curHex = 0;
2419         }
2420         else
2421         {
2422           vTool.GetFaceNodes( curFacet, facetNodes );
2423           vTool.Set( curHex );
2424           curFacet = vTool.GetFaceIndex( facetNodes );
2425         }
2426       }
2427       while ( curHex )
2428       {
2429         // store a facet to split
2430         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2431         {
2432           theFacets.insert( make_pair( curHex, -1 ));
2433           break;
2434         }
2435         if ( !allHex && !theHexas.count( curHex ))
2436           break;
2437
2438         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2439           theFacets.insert( make_pair( curHex, curFacet ));
2440         if ( !facetIt2isNew.second )
2441           break;
2442
2443         // remember not-to-split facets in facetsToCheck
2444         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2445         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2446         {
2447           if ( iF == curFacet && iF == oppFacet )
2448             continue;
2449           TVolumeFaceKey facetKey ( vTool, iF );
2450           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2451           pair< TFacetMap::iterator, bool > it2isnew =
2452             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2453           if ( !it2isnew.second )
2454             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2455         }
2456         // pass to a volume adjacent via oppFacet
2457         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2458         {
2459           curHex = 0;
2460         }
2461         else
2462         {
2463           // get a new curFacet
2464           vTool.GetFaceNodes( oppFacet, facetNodes );
2465           vTool.Set( curHex );
2466           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2467         }
2468       }
2469     } // move in two directions from startHex via facetID
2470
2471     // Find a new startHex by facetsToCheck
2472
2473     startHex = 0;
2474     facetID  = -1;
2475     TFacetMap::iterator fIt = facetsToCheck.begin();
2476     while ( !startHex && fIt != facetsToCheck.end() )
2477     {
2478       const TElemFacets&  elemFacets = fIt->second;
2479       const SMDS_MeshElement*    hex = elemFacets.first->first;
2480       int                 splitFacet = elemFacets.first->second;
2481       int               lateralFacet = elemFacets.second;
2482       facetsToCheck.erase( fIt );
2483       fIt = facetsToCheck.begin();
2484
2485       vTool.Set( hex );
2486       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2487            curHex->GetGeomType() != SMDSGeom_HEXA )
2488         continue;
2489       if ( !allHex && !theHexas.count( curHex ))
2490         continue;
2491
2492       startHex = curHex;
2493
2494       // find a facet of startHex to split 
2495
2496       set<const SMDS_MeshNode*> lateralNodes;
2497       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2498       vTool.GetFaceNodes( splitFacet,   facetNodes );
2499       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2500       vTool.Set( startHex );
2501       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2502
2503       // look for a facet of startHex having common nodes with facetNodes
2504       // but not lateralFacet
2505       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2506       {
2507         if ( iF == lateralFacet )
2508           continue;
2509         int nbCommonNodes = 0;
2510         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2511         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2512           nbCommonNodes += facetNodes.count( nn[ iN ]);
2513
2514         if ( nbCommonNodes >= 2 )
2515         {
2516           facetID = iF;
2517           break;
2518         }
2519       }
2520       if ( facetID < 0 )
2521         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2522     }
2523   } //   while ( startHex )
2524 }
2525
2526 //=======================================================================
2527 //function : AddToSameGroups
2528 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2529 //=======================================================================
2530
2531 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2532                                         const SMDS_MeshElement* elemInGroups,
2533                                         SMESHDS_Mesh *          aMesh)
2534 {
2535   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2536   if (!groups.empty()) {
2537     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2538     for ( ; grIt != groups.end(); grIt++ ) {
2539       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2540       if ( group && group->Contains( elemInGroups ))
2541         group->SMDSGroup().Add( elemToAdd );
2542     }
2543   }
2544 }
2545
2546
2547 //=======================================================================
2548 //function : RemoveElemFromGroups
2549 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2550 //=======================================================================
2551 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2552                                              SMESHDS_Mesh *          aMesh)
2553 {
2554   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2555   if (!groups.empty())
2556   {
2557     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2558     for (; GrIt != groups.end(); GrIt++)
2559     {
2560       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2561       if (!grp || grp->IsEmpty()) continue;
2562       grp->SMDSGroup().Remove(removeelem);
2563     }
2564   }
2565 }
2566
2567 //================================================================================
2568 /*!
2569  * \brief Replace elemToRm by elemToAdd in the all groups
2570  */
2571 //================================================================================
2572
2573 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2574                                             const SMDS_MeshElement* elemToAdd,
2575                                             SMESHDS_Mesh *          aMesh)
2576 {
2577   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2578   if (!groups.empty()) {
2579     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2580     for ( ; grIt != groups.end(); grIt++ ) {
2581       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2582       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2583         group->SMDSGroup().Add( elemToAdd );
2584     }
2585   }
2586 }
2587
2588 //================================================================================
2589 /*!
2590  * \brief Replace elemToRm by elemToAdd in the all groups
2591  */
2592 //================================================================================
2593
2594 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2595                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2596                                             SMESHDS_Mesh *                         aMesh)
2597 {
2598   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2599   if (!groups.empty())
2600   {
2601     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2602     for ( ; grIt != groups.end(); grIt++ ) {
2603       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2604       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2605         for ( int i = 0; i < elemToAdd.size(); ++i )
2606           group->SMDSGroup().Add( elemToAdd[ i ] );
2607     }
2608   }
2609 }
2610
2611 //=======================================================================
2612 //function : QuadToTri
2613 //purpose  : Cut quadrangles into triangles.
2614 //           theCrit is used to select a diagonal to cut
2615 //=======================================================================
2616
2617 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2618                                   const bool         the13Diag)
2619 {
2620   myLastCreatedElems.Clear();
2621   myLastCreatedNodes.Clear();
2622
2623   MESSAGE( "::QuadToTri()" );
2624
2625   SMESHDS_Mesh * aMesh = GetMeshDS();
2626
2627   Handle(Geom_Surface) surface;
2628   SMESH_MesherHelper   helper( *GetMesh() );
2629
2630   TIDSortedElemSet::iterator itElem;
2631   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2632     const SMDS_MeshElement* elem = *itElem;
2633     if ( !elem || elem->GetType() != SMDSAbs_Face )
2634       continue;
2635     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2636     if(!isquad) continue;
2637
2638     if(elem->NbNodes()==4) {
2639       // retrieve element nodes
2640       const SMDS_MeshNode* aNodes [4];
2641       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2642       int i = 0;
2643       while ( itN->more() )
2644         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2645
2646       int aShapeId = FindShape( elem );
2647       const SMDS_MeshElement* newElem1 = 0;
2648       const SMDS_MeshElement* newElem2 = 0;
2649       if ( the13Diag ) {
2650         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2651         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2652       }
2653       else {
2654         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2655         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2656       }
2657       myLastCreatedElems.Append(newElem1);
2658       myLastCreatedElems.Append(newElem2);
2659       // put a new triangle on the same shape and add to the same groups
2660       if ( aShapeId )
2661         {
2662           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2663           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2664         }
2665       AddToSameGroups( newElem1, elem, aMesh );
2666       AddToSameGroups( newElem2, elem, aMesh );
2667       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2668       aMesh->RemoveElement( elem );
2669     }
2670
2671     // Quadratic quadrangle
2672
2673     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2674
2675       // get surface elem is on
2676       int aShapeId = FindShape( elem );
2677       if ( aShapeId != helper.GetSubShapeID() ) {
2678         surface.Nullify();
2679         TopoDS_Shape shape;
2680         if ( aShapeId > 0 )
2681           shape = aMesh->IndexToShape( aShapeId );
2682         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2683           TopoDS_Face face = TopoDS::Face( shape );
2684           surface = BRep_Tool::Surface( face );
2685           if ( !surface.IsNull() )
2686             helper.SetSubShape( shape );
2687         }
2688       }
2689
2690       const SMDS_MeshNode* aNodes [8];
2691       const SMDS_MeshNode* inFaceNode = 0;
2692       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2693       int i = 0;
2694       while ( itN->more() ) {
2695         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2696         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2697              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2698         {
2699           inFaceNode = aNodes[ i-1 ];
2700         }
2701       }
2702
2703       // find middle point for (0,1,2,3)
2704       // and create a node in this point;
2705       gp_XYZ p( 0,0,0 );
2706       if ( surface.IsNull() ) {
2707         for(i=0; i<4; i++)
2708           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2709         p /= 4;
2710       }
2711       else {
2712         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2713         gp_XY uv( 0,0 );
2714         for(i=0; i<4; i++)
2715           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2716         uv /= 4.;
2717         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2718       }
2719       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2720       myLastCreatedNodes.Append(newN);
2721
2722       // create a new element
2723       const SMDS_MeshElement* newElem1 = 0;
2724       const SMDS_MeshElement* newElem2 = 0;
2725       if ( the13Diag ) {
2726         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2727                                   aNodes[6], aNodes[7], newN );
2728         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2729                                   newN,      aNodes[4], aNodes[5] );
2730       }
2731       else {
2732         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2733                                   aNodes[7], aNodes[4], newN );
2734         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2735                                   newN,      aNodes[5], aNodes[6] );
2736       }
2737       myLastCreatedElems.Append(newElem1);
2738       myLastCreatedElems.Append(newElem2);
2739       // put a new triangle on the same shape and add to the same groups
2740       if ( aShapeId )
2741         {
2742           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2743           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2744         }
2745       AddToSameGroups( newElem1, elem, aMesh );
2746       AddToSameGroups( newElem2, elem, aMesh );
2747       aMesh->RemoveElement( elem );
2748     }
2749   }
2750
2751   return true;
2752 }
2753
2754 //=======================================================================
2755 //function : getAngle
2756 //purpose  :
2757 //=======================================================================
2758
2759 double getAngle(const SMDS_MeshElement * tr1,
2760                 const SMDS_MeshElement * tr2,
2761                 const SMDS_MeshNode *    n1,
2762                 const SMDS_MeshNode *    n2)
2763 {
2764   double angle = 2. * M_PI; // bad angle
2765
2766   // get normals
2767   SMESH::Controls::TSequenceOfXYZ P1, P2;
2768   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2769        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2770     return angle;
2771   gp_Vec N1,N2;
2772   if(!tr1->IsQuadratic())
2773     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2774   else
2775     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2776   if ( N1.SquareMagnitude() <= gp::Resolution() )
2777     return angle;
2778   if(!tr2->IsQuadratic())
2779     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2780   else
2781     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2782   if ( N2.SquareMagnitude() <= gp::Resolution() )
2783     return angle;
2784
2785   // find the first diagonal node n1 in the triangles:
2786   // take in account a diagonal link orientation
2787   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2788   for ( int t = 0; t < 2; t++ ) {
2789     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2790     int i = 0, iDiag = -1;
2791     while ( it->more()) {
2792       const SMDS_MeshElement *n = it->next();
2793       if ( n == n1 || n == n2 ) {
2794         if ( iDiag < 0)
2795           iDiag = i;
2796         else {
2797           if ( i - iDiag == 1 )
2798             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2799           else
2800             nFirst[ t ] = n;
2801           break;
2802         }
2803       }
2804       i++;
2805     }
2806   }
2807   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2808     N2.Reverse();
2809
2810   angle = N1.Angle( N2 );
2811   //SCRUTE( angle );
2812   return angle;
2813 }
2814
2815 // =================================================
2816 // class generating a unique ID for a pair of nodes
2817 // and able to return nodes by that ID
2818 // =================================================
2819 class LinkID_Gen {
2820 public:
2821
2822   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2823     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2824   {}
2825
2826   long GetLinkID (const SMDS_MeshNode * n1,
2827                   const SMDS_MeshNode * n2) const
2828   {
2829     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2830   }
2831
2832   bool GetNodes (const long             theLinkID,
2833                  const SMDS_MeshNode* & theNode1,
2834                  const SMDS_MeshNode* & theNode2) const
2835   {
2836     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2837     if ( !theNode1 ) return false;
2838     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2839     if ( !theNode2 ) return false;
2840     return true;
2841   }
2842
2843 private:
2844   LinkID_Gen();
2845   const SMESHDS_Mesh* myMesh;
2846   long                myMaxID;
2847 };
2848
2849
2850 //=======================================================================
2851 //function : TriToQuad
2852 //purpose  : Fuse neighbour triangles into quadrangles.
2853 //           theCrit is used to select a neighbour to fuse with.
2854 //           theMaxAngle is a max angle between element normals at which
2855 //           fusion is still performed.
2856 //=======================================================================
2857
2858 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2859                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2860                                   const double                         theMaxAngle)
2861 {
2862   myLastCreatedElems.Clear();
2863   myLastCreatedNodes.Clear();
2864
2865   MESSAGE( "::TriToQuad()" );
2866
2867   if ( !theCrit.get() )
2868     return false;
2869
2870   SMESHDS_Mesh * aMesh = GetMeshDS();
2871
2872   // Prepare data for algo: build
2873   // 1. map of elements with their linkIDs
2874   // 2. map of linkIDs with their elements
2875
2876   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2877   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2878   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2879   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2880
2881   TIDSortedElemSet::iterator itElem;
2882   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2883   {
2884     const SMDS_MeshElement* elem = *itElem;
2885     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2886     bool IsTria = ( elem->NbCornerNodes()==3 );
2887     if (!IsTria) continue;
2888
2889     // retrieve element nodes
2890     const SMDS_MeshNode* aNodes [4];
2891     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2892     int i = 0;
2893     while ( i < 3 )
2894       aNodes[ i++ ] = itN->next();
2895     aNodes[ 3 ] = aNodes[ 0 ];
2896
2897     // fill maps
2898     for ( i = 0; i < 3; i++ ) {
2899       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2900       // check if elements sharing a link can be fused
2901       itLE = mapLi_listEl.find( link );
2902       if ( itLE != mapLi_listEl.end() ) {
2903         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2904           continue;
2905         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2906         //if ( FindShape( elem ) != FindShape( elem2 ))
2907         //  continue; // do not fuse triangles laying on different shapes
2908         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2909           continue; // avoid making badly shaped quads
2910         (*itLE).second.push_back( elem );
2911       }
2912       else {
2913         mapLi_listEl[ link ].push_back( elem );
2914       }
2915       mapEl_setLi [ elem ].insert( link );
2916     }
2917   }
2918   // Clean the maps from the links shared by a sole element, ie
2919   // links to which only one element is bound in mapLi_listEl
2920
2921   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2922     int nbElems = (*itLE).second.size();
2923     if ( nbElems < 2  ) {
2924       const SMDS_MeshElement* elem = (*itLE).second.front();
2925       SMESH_TLink link = (*itLE).first;
2926       mapEl_setLi[ elem ].erase( link );
2927       if ( mapEl_setLi[ elem ].empty() )
2928         mapEl_setLi.erase( elem );
2929     }
2930   }
2931
2932   // Algo: fuse triangles into quadrangles
2933
2934   while ( ! mapEl_setLi.empty() ) {
2935     // Look for the start element:
2936     // the element having the least nb of shared links
2937     const SMDS_MeshElement* startElem = 0;
2938     int minNbLinks = 4;
2939     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2940       int nbLinks = (*itEL).second.size();
2941       if ( nbLinks < minNbLinks ) {
2942         startElem = (*itEL).first;
2943         minNbLinks = nbLinks;
2944         if ( minNbLinks == 1 )
2945           break;
2946       }
2947     }
2948
2949     // search elements to fuse starting from startElem or links of elements
2950     // fused earlyer - startLinks
2951     list< SMESH_TLink > startLinks;
2952     while ( startElem || !startLinks.empty() ) {
2953       while ( !startElem && !startLinks.empty() ) {
2954         // Get an element to start, by a link
2955         SMESH_TLink linkId = startLinks.front();
2956         startLinks.pop_front();
2957         itLE = mapLi_listEl.find( linkId );
2958         if ( itLE != mapLi_listEl.end() ) {
2959           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2960           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2961           for ( ; itE != listElem.end() ; itE++ )
2962             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2963               startElem = (*itE);
2964           mapLi_listEl.erase( itLE );
2965         }
2966       }
2967
2968       if ( startElem ) {
2969         // Get candidates to be fused
2970         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2971         const SMESH_TLink *link12, *link13;
2972         startElem = 0;
2973         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2974         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2975         ASSERT( !setLi.empty() );
2976         set< SMESH_TLink >::iterator itLi;
2977         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2978         {
2979           const SMESH_TLink & link = (*itLi);
2980           itLE = mapLi_listEl.find( link );
2981           if ( itLE == mapLi_listEl.end() )
2982             continue;
2983
2984           const SMDS_MeshElement* elem = (*itLE).second.front();
2985           if ( elem == tr1 )
2986             elem = (*itLE).second.back();
2987           mapLi_listEl.erase( itLE );
2988           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2989             continue;
2990           if ( tr2 ) {
2991             tr3 = elem;
2992             link13 = &link;
2993           }
2994           else {
2995             tr2 = elem;
2996             link12 = &link;
2997           }
2998
2999           // add other links of elem to list of links to re-start from
3000           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3001           set< SMESH_TLink >::iterator it;
3002           for ( it = links.begin(); it != links.end(); it++ ) {
3003             const SMESH_TLink& link2 = (*it);
3004             if ( link2 != link )
3005               startLinks.push_back( link2 );
3006           }
3007         }
3008
3009         // Get nodes of possible quadrangles
3010         const SMDS_MeshNode *n12 [4], *n13 [4];
3011         bool Ok12 = false, Ok13 = false;
3012         const SMDS_MeshNode *linkNode1, *linkNode2;
3013         if(tr2) {
3014           linkNode1 = link12->first;
3015           linkNode2 = link12->second;
3016           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3017             Ok12 = true;
3018         }
3019         if(tr3) {
3020           linkNode1 = link13->first;
3021           linkNode2 = link13->second;
3022           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3023             Ok13 = true;
3024         }
3025
3026         // Choose a pair to fuse
3027         if ( Ok12 && Ok13 ) {
3028           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3029           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3030           double aBadRate12 = getBadRate( &quad12, theCrit );
3031           double aBadRate13 = getBadRate( &quad13, theCrit );
3032           if (  aBadRate13 < aBadRate12 )
3033             Ok12 = false;
3034           else
3035             Ok13 = false;
3036         }
3037
3038         // Make quadrangles
3039         // and remove fused elems and remove links from the maps
3040         mapEl_setLi.erase( tr1 );
3041         if ( Ok12 )
3042         {
3043           mapEl_setLi.erase( tr2 );
3044           mapLi_listEl.erase( *link12 );
3045           if ( tr1->NbNodes() == 3 )
3046           {
3047             const SMDS_MeshElement* newElem = 0;
3048             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3049             myLastCreatedElems.Append(newElem);
3050             AddToSameGroups( newElem, tr1, aMesh );
3051             int aShapeId = tr1->getshapeId();
3052             if ( aShapeId )
3053               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3054             aMesh->RemoveElement( tr1 );
3055             aMesh->RemoveElement( tr2 );
3056           }
3057           else {
3058             vector< const SMDS_MeshNode* > N1;
3059             vector< const SMDS_MeshNode* > N2;
3060             getNodesFromTwoTria(tr1,tr2,N1,N2);
3061             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3062             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3063             // i.e. first nodes from both arrays form a new diagonal
3064             const SMDS_MeshNode* aNodes[8];
3065             aNodes[0] = N1[0];
3066             aNodes[1] = N1[1];
3067             aNodes[2] = N2[0];
3068             aNodes[3] = N2[1];
3069             aNodes[4] = N1[3];
3070             aNodes[5] = N2[5];
3071             aNodes[6] = N2[3];
3072             aNodes[7] = N1[5];
3073             const SMDS_MeshElement* newElem = 0;
3074             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3075               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3076                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3077             else
3078               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3079                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3080             myLastCreatedElems.Append(newElem);
3081             AddToSameGroups( newElem, tr1, aMesh );
3082             int aShapeId = tr1->getshapeId();
3083             if ( aShapeId )
3084               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3085             aMesh->RemoveElement( tr1 );
3086             aMesh->RemoveElement( tr2 );
3087             // remove middle node (9)
3088             if ( N1[4]->NbInverseElements() == 0 )
3089               aMesh->RemoveNode( N1[4] );
3090             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3091               aMesh->RemoveNode( N1[6] );
3092             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3093               aMesh->RemoveNode( N2[6] );
3094           }
3095         }
3096         else if ( Ok13 )
3097         {
3098           mapEl_setLi.erase( tr3 );
3099           mapLi_listEl.erase( *link13 );
3100           if ( tr1->NbNodes() == 3 ) {
3101             const SMDS_MeshElement* newElem = 0;
3102             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3103             myLastCreatedElems.Append(newElem);
3104             AddToSameGroups( newElem, tr1, aMesh );
3105             int aShapeId = tr1->getshapeId();
3106             if ( aShapeId )
3107               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3108             aMesh->RemoveElement( tr1 );
3109             aMesh->RemoveElement( tr3 );
3110           }
3111           else {
3112             vector< const SMDS_MeshNode* > N1;
3113             vector< const SMDS_MeshNode* > N2;
3114             getNodesFromTwoTria(tr1,tr3,N1,N2);
3115             // now we receive following N1 and N2 (using numeration as above image)
3116             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3117             // i.e. first nodes from both arrays form a new diagonal
3118             const SMDS_MeshNode* aNodes[8];
3119             aNodes[0] = N1[0];
3120             aNodes[1] = N1[1];
3121             aNodes[2] = N2[0];
3122             aNodes[3] = N2[1];
3123             aNodes[4] = N1[3];
3124             aNodes[5] = N2[5];
3125             aNodes[6] = N2[3];
3126             aNodes[7] = N1[5];
3127             const SMDS_MeshElement* newElem = 0;
3128             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3129               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3130                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3131             else
3132               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3133                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3134             myLastCreatedElems.Append(newElem);
3135             AddToSameGroups( newElem, tr1, aMesh );
3136             int aShapeId = tr1->getshapeId();
3137             if ( aShapeId )
3138               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3139             aMesh->RemoveElement( tr1 );
3140             aMesh->RemoveElement( tr3 );
3141             // remove middle node (9)
3142             if ( N1[4]->NbInverseElements() == 0 )
3143               aMesh->RemoveNode( N1[4] );
3144             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3145               aMesh->RemoveNode( N1[6] );
3146             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3147               aMesh->RemoveNode( N2[6] );
3148           }
3149         }
3150
3151         // Next element to fuse: the rejected one
3152         if ( tr3 )
3153           startElem = Ok12 ? tr3 : tr2;
3154
3155       } // if ( startElem )
3156     } // while ( startElem || !startLinks.empty() )
3157   } // while ( ! mapEl_setLi.empty() )
3158
3159   return true;
3160 }
3161
3162
3163 /*#define DUMPSO(txt) \
3164 //  cout << txt << endl;
3165 //=============================================================================
3166 //
3167 //
3168 //
3169 //=============================================================================
3170 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3171 {
3172 if ( i1 == i2 )
3173 return;
3174 int tmp = idNodes[ i1 ];
3175 idNodes[ i1 ] = idNodes[ i2 ];
3176 idNodes[ i2 ] = tmp;
3177 gp_Pnt Ptmp = P[ i1 ];
3178 P[ i1 ] = P[ i2 ];
3179 P[ i2 ] = Ptmp;
3180 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3181 }
3182
3183 //=======================================================================
3184 //function : SortQuadNodes
3185 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3186 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3187 //           1 or 2 else 0.
3188 //=======================================================================
3189
3190 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3191 int               idNodes[] )
3192 {
3193   gp_Pnt P[4];
3194   int i;
3195   for ( i = 0; i < 4; i++ ) {
3196     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3197     if ( !n ) return 0;
3198     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3199   }
3200
3201   gp_Vec V1(P[0], P[1]);
3202   gp_Vec V2(P[0], P[2]);
3203   gp_Vec V3(P[0], P[3]);
3204
3205   gp_Vec Cross1 = V1 ^ V2;
3206   gp_Vec Cross2 = V2 ^ V3;
3207
3208   i = 0;
3209   if (Cross1.Dot(Cross2) < 0)
3210   {
3211     Cross1 = V2 ^ V1;
3212     Cross2 = V1 ^ V3;
3213
3214     if (Cross1.Dot(Cross2) < 0)
3215       i = 2;
3216     else
3217       i = 1;
3218     swap ( i, i + 1, idNodes, P );
3219
3220     //     for ( int ii = 0; ii < 4; ii++ ) {
3221     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3222     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3223     //     }
3224   }
3225   return i;
3226 }
3227
3228 //=======================================================================
3229 //function : SortHexaNodes
3230 //purpose  : Set 8 nodes of a hexahedron in a good order.
3231 //           Return success status
3232 //=======================================================================
3233
3234 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3235                                       int               idNodes[] )
3236 {
3237   gp_Pnt P[8];
3238   int i;
3239   DUMPSO( "INPUT: ========================================");
3240   for ( i = 0; i < 8; i++ ) {
3241     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3242     if ( !n ) return false;
3243     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3244     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3245   }
3246   DUMPSO( "========================================");
3247
3248
3249   set<int> faceNodes;  // ids of bottom face nodes, to be found
3250   set<int> checkedId1; // ids of tried 2-nd nodes
3251   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3252   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3253   int iMin, iLoop1 = 0;
3254
3255   // Loop to try the 2-nd nodes
3256
3257   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3258   {
3259     // Find not checked 2-nd node
3260     for ( i = 1; i < 8; i++ )
3261       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3262         int id1 = idNodes[i];
3263         swap ( 1, i, idNodes, P );
3264         checkedId1.insert ( id1 );
3265         break;
3266       }
3267
3268     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3269     // ie that all but meybe one (id3 which is on the same face) nodes
3270     // lay on the same side from the triangle plane.
3271
3272     bool manyInPlane = false; // more than 4 nodes lay in plane
3273     int iLoop2 = 0;
3274     while ( ++iLoop2 < 6 ) {
3275
3276       // get 1-2-3 plane coeffs
3277       Standard_Real A, B, C, D;
3278       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3279       if ( N.SquareMagnitude() > gp::Resolution() )
3280       {
3281         gp_Pln pln ( P[0], N );
3282         pln.Coefficients( A, B, C, D );
3283
3284         // find the node (iMin) closest to pln
3285         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3286         set<int> idInPln;
3287         for ( i = 3; i < 8; i++ ) {
3288           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3289           if ( fabs( dist[i] ) < minDist ) {
3290             minDist = fabs( dist[i] );
3291             iMin = i;
3292           }
3293           if ( fabs( dist[i] ) <= tol )
3294             idInPln.insert( idNodes[i] );
3295         }
3296
3297         // there should not be more than 4 nodes in bottom plane
3298         if ( idInPln.size() > 1 )
3299         {
3300           DUMPSO( "### idInPln.size() = " << idInPln.size());
3301           // idInPlane does not contain the first 3 nodes
3302           if ( manyInPlane || idInPln.size() == 5)
3303             return false; // all nodes in one plane
3304           manyInPlane = true;
3305
3306           // set the 1-st node to be not in plane
3307           for ( i = 3; i < 8; i++ ) {
3308             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3309               DUMPSO( "### Reset 0-th node");
3310               swap( 0, i, idNodes, P );
3311               break;
3312             }
3313           }
3314
3315           // reset to re-check second nodes
3316           leastDist = DBL_MAX;
3317           faceNodes.clear();
3318           checkedId1.clear();
3319           iLoop1 = 0;
3320           break; // from iLoop2;
3321         }
3322
3323         // check that the other 4 nodes are on the same side
3324         bool sameSide = true;
3325         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3326         for ( i = 3; sameSide && i < 8; i++ ) {
3327           if ( i != iMin )
3328             sameSide = ( isNeg == dist[i] <= 0.);
3329         }
3330
3331         // keep best solution
3332         if ( sameSide && minDist < leastDist ) {
3333           leastDist = minDist;
3334           faceNodes.clear();
3335           faceNodes.insert( idNodes[ 1 ] );
3336           faceNodes.insert( idNodes[ 2 ] );
3337           faceNodes.insert( idNodes[ iMin ] );
3338           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3339                   << " leastDist = " << leastDist);
3340           if ( leastDist <= DBL_MIN )
3341             break;
3342         }
3343       }
3344
3345       // set next 3-d node to check
3346       int iNext = 2 + iLoop2;
3347       if ( iNext < 8 ) {
3348         DUMPSO( "Try 2-nd");
3349         swap ( 2, iNext, idNodes, P );
3350       }
3351     } // while ( iLoop2 < 6 )
3352   } // iLoop1
3353
3354   if ( faceNodes.empty() ) return false;
3355
3356   // Put the faceNodes in proper places
3357   for ( i = 4; i < 8; i++ ) {
3358     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3359       // find a place to put
3360       int iTo = 1;
3361       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3362         iTo++;
3363       DUMPSO( "Set faceNodes");
3364       swap ( iTo, i, idNodes, P );
3365     }
3366   }
3367
3368
3369   // Set nodes of the found bottom face in good order
3370   DUMPSO( " Found bottom face: ");
3371   i = SortQuadNodes( theMesh, idNodes );
3372   if ( i ) {
3373     gp_Pnt Ptmp = P[ i ];
3374     P[ i ] = P[ i+1 ];
3375     P[ i+1 ] = Ptmp;
3376   }
3377   //   else
3378   //     for ( int ii = 0; ii < 4; ii++ ) {
3379   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3380   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3381   //    }
3382
3383   // Gravity center of the top and bottom faces
3384   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3385   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3386
3387   // Get direction from the bottom to the top face
3388   gp_Vec upDir ( aGCb, aGCt );
3389   Standard_Real upDirSize = upDir.Magnitude();
3390   if ( upDirSize <= gp::Resolution() ) return false;
3391   upDir / upDirSize;
3392
3393   // Assure that the bottom face normal points up
3394   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3395   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3396   if ( Nb.Dot( upDir ) < 0 ) {
3397     DUMPSO( "Reverse bottom face");
3398     swap( 1, 3, idNodes, P );
3399   }
3400
3401   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3402   Standard_Real minDist = DBL_MAX;
3403   for ( i = 4; i < 8; i++ ) {
3404     // projection of P[i] to the plane defined by P[0] and upDir
3405     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3406     Standard_Real sqDist = P[0].SquareDistance( Pp );
3407     if ( sqDist < minDist ) {
3408       minDist = sqDist;
3409       iMin = i;
3410     }
3411   }
3412   DUMPSO( "Set 4-th");
3413   swap ( 4, iMin, idNodes, P );
3414
3415   // Set nodes of the top face in good order
3416   DUMPSO( "Sort top face");
3417   i = SortQuadNodes( theMesh, &idNodes[4] );
3418   if ( i ) {
3419     i += 4;
3420     gp_Pnt Ptmp = P[ i ];
3421     P[ i ] = P[ i+1 ];
3422     P[ i+1 ] = Ptmp;
3423   }
3424
3425   // Assure that direction of the top face normal is from the bottom face
3426   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3427   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3428   if ( Nt.Dot( upDir ) < 0 ) {
3429     DUMPSO( "Reverse top face");
3430     swap( 5, 7, idNodes, P );
3431   }
3432
3433   //   DUMPSO( "OUTPUT: ========================================");
3434   //   for ( i = 0; i < 8; i++ ) {
3435   //     float *p = ugrid->GetPoint(idNodes[i]);
3436   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3437   //   }
3438
3439   return true;
3440 }*/
3441
3442 //================================================================================
3443 /*!
3444  * \brief Return nodes linked to the given one
3445  * \param theNode - the node
3446  * \param linkedNodes - the found nodes
3447  * \param type - the type of elements to check
3448  *
3449  * Medium nodes are ignored
3450  */
3451 //================================================================================
3452
3453 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3454                                        TIDSortedElemSet &   linkedNodes,
3455                                        SMDSAbs_ElementType  type )
3456 {
3457   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3458   while ( elemIt->more() )
3459   {
3460     const SMDS_MeshElement* elem = elemIt->next();
3461     if(elem->GetType() == SMDSAbs_0DElement)
3462       continue;
3463
3464     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3465     if ( elem->GetType() == SMDSAbs_Volume )
3466     {
3467       SMDS_VolumeTool vol( elem );
3468       while ( nodeIt->more() ) {
3469         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3470         if ( theNode != n && vol.IsLinked( theNode, n ))
3471           linkedNodes.insert( n );
3472       }
3473     }
3474     else
3475     {
3476       for ( int i = 0; nodeIt->more(); ++i ) {
3477         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3478         if ( n == theNode ) {
3479           int iBefore = i - 1;
3480           int iAfter  = i + 1;
3481           if ( elem->IsQuadratic() ) {
3482             int nb = elem->NbNodes() / 2;
3483             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3484             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3485           }
3486           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3487           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3488         }
3489       }
3490     }
3491   }
3492 }
3493
3494 //=======================================================================
3495 //function : laplacianSmooth
3496 //purpose  : pulls theNode toward the center of surrounding nodes directly
3497 //           connected to that node along an element edge
3498 //=======================================================================
3499
3500 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3501                      const Handle(Geom_Surface)&          theSurface,
3502                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3503 {
3504   // find surrounding nodes
3505
3506   TIDSortedElemSet nodeSet;
3507   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3508
3509   // compute new coodrs
3510
3511   double coord[] = { 0., 0., 0. };
3512   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3513   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3514     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3515     if ( theSurface.IsNull() ) { // smooth in 3D
3516       coord[0] += node->X();
3517       coord[1] += node->Y();
3518       coord[2] += node->Z();
3519     }
3520     else { // smooth in 2D
3521       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3522       gp_XY* uv = theUVMap[ node ];
3523       coord[0] += uv->X();
3524       coord[1] += uv->Y();
3525     }
3526   }
3527   int nbNodes = nodeSet.size();
3528   if ( !nbNodes )
3529     return;
3530   coord[0] /= nbNodes;
3531   coord[1] /= nbNodes;
3532
3533   if ( !theSurface.IsNull() ) {
3534     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3535     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3536     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3537     coord[0] = p3d.X();
3538     coord[1] = p3d.Y();
3539     coord[2] = p3d.Z();
3540   }
3541   else
3542     coord[2] /= nbNodes;
3543
3544   // move node
3545
3546   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3547 }
3548
3549 //=======================================================================
3550 //function : centroidalSmooth
3551 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3552 //           surrounding elements
3553 //=======================================================================
3554
3555 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3556                       const Handle(Geom_Surface)&          theSurface,
3557                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3558 {
3559   gp_XYZ aNewXYZ(0.,0.,0.);
3560   SMESH::Controls::Area anAreaFunc;
3561   double totalArea = 0.;
3562   int nbElems = 0;
3563
3564   // compute new XYZ
3565
3566   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3567   while ( elemIt->more() )
3568   {
3569     const SMDS_MeshElement* elem = elemIt->next();
3570     nbElems++;
3571
3572     gp_XYZ elemCenter(0.,0.,0.);
3573     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3574     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3575     int nn = elem->NbNodes();
3576     if(elem->IsQuadratic()) nn = nn/2;
3577     int i=0;
3578     //while ( itN->more() ) {
3579     while ( i<nn ) {
3580       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3581       i++;
3582       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3583       aNodePoints.push_back( aP );
3584       if ( !theSurface.IsNull() ) { // smooth in 2D
3585         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3586         gp_XY* uv = theUVMap[ aNode ];
3587         aP.SetCoord( uv->X(), uv->Y(), 0. );
3588       }
3589       elemCenter += aP;
3590     }
3591     double elemArea = anAreaFunc.GetValue( aNodePoints );
3592     totalArea += elemArea;
3593     elemCenter /= nn;
3594     aNewXYZ += elemCenter * elemArea;
3595   }
3596   aNewXYZ /= totalArea;
3597   if ( !theSurface.IsNull() ) {
3598     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3599     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3600   }
3601
3602   // move node
3603
3604   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3605 }
3606
3607 //=======================================================================
3608 //function : getClosestUV
3609 //purpose  : return UV of closest projection
3610 //=======================================================================
3611
3612 static bool getClosestUV (Extrema_GenExtPS& projector,
3613                           const gp_Pnt&     point,
3614                           gp_XY &           result)
3615 {
3616   projector.Perform( point );
3617   if ( projector.IsDone() ) {
3618     double u, v, minVal = DBL_MAX;
3619     for ( int i = projector.NbExt(); i > 0; i-- )
3620 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3621       if ( projector.SquareDistance( i ) < minVal ) {
3622         minVal = projector.SquareDistance( i );
3623 #else
3624       if ( projector.Value( i ) < minVal ) {
3625         minVal = projector.Value( i );
3626 #endif
3627         projector.Point( i ).Parameter( u, v );
3628       }
3629     result.SetCoord( u, v );
3630     return true;
3631   }
3632   return false;
3633 }
3634
3635 //=======================================================================
3636 //function : Smooth
3637 //purpose  : Smooth theElements during theNbIterations or until a worst
3638 //           element has aspect ratio <= theTgtAspectRatio.
3639 //           Aspect Ratio varies in range [1.0, inf].
3640 //           If theElements is empty, the whole mesh is smoothed.
3641 //           theFixedNodes contains additionally fixed nodes. Nodes built
3642 //           on edges and boundary nodes are always fixed.
3643 //=======================================================================
3644
3645 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3646                                set<const SMDS_MeshNode*> & theFixedNodes,
3647                                const SmoothMethod          theSmoothMethod,
3648                                const int                   theNbIterations,
3649                                double                      theTgtAspectRatio,
3650                                const bool                  the2D)
3651 {
3652   myLastCreatedElems.Clear();
3653   myLastCreatedNodes.Clear();
3654
3655   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3656
3657   if ( theTgtAspectRatio < 1.0 )
3658     theTgtAspectRatio = 1.0;
3659
3660   const double disttol = 1.e-16;
3661
3662   SMESH::Controls::AspectRatio aQualityFunc;
3663
3664   SMESHDS_Mesh* aMesh = GetMeshDS();
3665
3666   if ( theElems.empty() ) {
3667     // add all faces to theElems
3668     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3669     while ( fIt->more() ) {
3670       const SMDS_MeshElement* face = fIt->next();
3671       theElems.insert( theElems.end(), face );
3672     }
3673   }
3674   // get all face ids theElems are on
3675   set< int > faceIdSet;
3676   TIDSortedElemSet::iterator itElem;
3677   if ( the2D )
3678     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3679       int fId = FindShape( *itElem );
3680       // check that corresponding submesh exists and a shape is face
3681       if (fId &&
3682           faceIdSet.find( fId ) == faceIdSet.end() &&
3683           aMesh->MeshElements( fId )) {
3684         TopoDS_Shape F = aMesh->IndexToShape( fId );
3685         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3686           faceIdSet.insert( fId );
3687       }
3688     }
3689   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3690
3691   // ===============================================
3692   // smooth elements on each TopoDS_Face separately
3693   // ===============================================
3694
3695   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3696   for ( ; fId != faceIdSet.rend(); ++fId ) {
3697     // get face surface and submesh
3698     Handle(Geom_Surface) surface;
3699     SMESHDS_SubMesh* faceSubMesh = 0;
3700     TopoDS_Face face;
3701     double fToler2 = 0, f,l;
3702     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3703     bool isUPeriodic = false, isVPeriodic = false;
3704     if ( *fId ) {
3705       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3706       surface = BRep_Tool::Surface( face );
3707       faceSubMesh = aMesh->MeshElements( *fId );
3708       fToler2 = BRep_Tool::Tolerance( face );
3709       fToler2 *= fToler2 * 10.;
3710       isUPeriodic = surface->IsUPeriodic();
3711       if ( isUPeriodic )
3712         surface->UPeriod();
3713       isVPeriodic = surface->IsVPeriodic();
3714       if ( isVPeriodic )
3715         surface->VPeriod();
3716       surface->Bounds( u1, u2, v1, v2 );
3717     }
3718     // ---------------------------------------------------------
3719     // for elements on a face, find movable and fixed nodes and
3720     // compute UV for them
3721     // ---------------------------------------------------------
3722     bool checkBoundaryNodes = false;
3723     bool isQuadratic = false;
3724     set<const SMDS_MeshNode*> setMovableNodes;
3725     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3726     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3727     list< const SMDS_MeshElement* > elemsOnFace;
3728
3729     Extrema_GenExtPS projector;
3730     GeomAdaptor_Surface surfAdaptor;
3731     if ( !surface.IsNull() ) {
3732       surfAdaptor.Load( surface );
3733       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3734     }
3735     int nbElemOnFace = 0;
3736     itElem = theElems.begin();
3737     // loop on not yet smoothed elements: look for elems on a face
3738     while ( itElem != theElems.end() ) {
3739       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3740         break; // all elements found
3741
3742       const SMDS_MeshElement* elem = *itElem;
3743       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3744            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3745         ++itElem;
3746         continue;
3747       }
3748       elemsOnFace.push_back( elem );
3749       theElems.erase( itElem++ );
3750       nbElemOnFace++;
3751
3752       if ( !isQuadratic )
3753         isQuadratic = elem->IsQuadratic();
3754
3755       // get movable nodes of elem
3756       const SMDS_MeshNode* node;
3757       SMDS_TypeOfPosition posType;
3758       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3759       int nn = 0, nbn =  elem->NbNodes();
3760       if(elem->IsQuadratic())
3761         nbn = nbn/2;
3762       while ( nn++ < nbn ) {
3763         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3764         const SMDS_PositionPtr& pos = node->GetPosition();
3765         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3766         if (posType != SMDS_TOP_EDGE &&
3767             posType != SMDS_TOP_VERTEX &&
3768             theFixedNodes.find( node ) == theFixedNodes.end())
3769         {
3770           // check if all faces around the node are on faceSubMesh
3771           // because a node on edge may be bound to face
3772           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3773           bool all = true;
3774           if ( faceSubMesh ) {
3775             while ( eIt->more() && all ) {
3776               const SMDS_MeshElement* e = eIt->next();
3777               all = faceSubMesh->Contains( e );
3778             }
3779           }
3780           if ( all )
3781             setMovableNodes.insert( node );
3782           else
3783             checkBoundaryNodes = true;
3784         }
3785         if ( posType == SMDS_TOP_3DSPACE )
3786           checkBoundaryNodes = true;
3787       }
3788
3789       if ( surface.IsNull() )
3790         continue;
3791
3792       // get nodes to check UV
3793       list< const SMDS_MeshNode* > uvCheckNodes;
3794       itN = elem->nodesIterator();
3795       nn = 0; nbn =  elem->NbNodes();
3796       if(elem->IsQuadratic())
3797         nbn = nbn/2;
3798       while ( nn++ < nbn ) {
3799         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3800         if ( uvMap.find( node ) == uvMap.end() )
3801           uvCheckNodes.push_back( node );
3802         // add nodes of elems sharing node
3803         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3804         //         while ( eIt->more() ) {
3805         //           const SMDS_MeshElement* e = eIt->next();
3806         //           if ( e != elem ) {
3807         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3808         //             while ( nIt->more() ) {
3809         //               const SMDS_MeshNode* n =
3810         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3811         //               if ( uvMap.find( n ) == uvMap.end() )
3812         //                 uvCheckNodes.push_back( n );
3813         //             }
3814         //           }
3815         //         }
3816       }
3817       // check UV on face
3818       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3819       for ( ; n != uvCheckNodes.end(); ++n ) {
3820         node = *n;
3821         gp_XY uv( 0, 0 );
3822         const SMDS_PositionPtr& pos = node->GetPosition();
3823         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3824         // get existing UV
3825         switch ( posType ) {
3826         case SMDS_TOP_FACE: {
3827           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3828           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3829           break;
3830         }
3831         case SMDS_TOP_EDGE: {
3832           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3833           Handle(Geom2d_Curve) pcurve;
3834           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3835             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3836           if ( !pcurve.IsNull() ) {
3837             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3838             uv = pcurve->Value( u ).XY();
3839           }
3840           break;
3841         }
3842         case SMDS_TOP_VERTEX: {
3843           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3844           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3845             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3846           break;
3847         }
3848         default:;
3849         }
3850         // check existing UV
3851         bool project = true;
3852         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3853         double dist1 = DBL_MAX, dist2 = 0;
3854         if ( posType != SMDS_TOP_3DSPACE ) {
3855           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3856           project = dist1 > fToler2;
3857         }
3858         if ( project ) { // compute new UV
3859           gp_XY newUV;
3860           if ( !getClosestUV( projector, pNode, newUV )) {
3861             MESSAGE("Node Projection Failed " << node);
3862           }
3863           else {
3864             if ( isUPeriodic )
3865               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3866             if ( isVPeriodic )
3867               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3868             // check new UV
3869             if ( posType != SMDS_TOP_3DSPACE )
3870               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3871             if ( dist2 < dist1 )
3872               uv = newUV;
3873           }
3874         }
3875         // store UV in the map
3876         listUV.push_back( uv );
3877         uvMap.insert( make_pair( node, &listUV.back() ));
3878       }
3879     } // loop on not yet smoothed elements
3880
3881     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3882       checkBoundaryNodes = true;
3883
3884     // fix nodes on mesh boundary
3885
3886     if ( checkBoundaryNodes ) {
3887       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3888       map< SMESH_TLink, int >::iterator link_nb;
3889       // put all elements links to linkNbMap
3890       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3891       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3892         const SMDS_MeshElement* elem = (*elemIt);
3893         int nbn =  elem->NbCornerNodes();
3894         // loop on elem links: insert them in linkNbMap
3895         for ( int iN = 0; iN < nbn; ++iN ) {
3896           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3897           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3898           SMESH_TLink link( n1, n2 );
3899           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3900           link_nb->second++;
3901         }
3902       }
3903       // remove nodes that are in links encountered only once from setMovableNodes
3904       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3905         if ( link_nb->second == 1 ) {
3906           setMovableNodes.erase( link_nb->first.node1() );
3907           setMovableNodes.erase( link_nb->first.node2() );
3908         }
3909       }
3910     }
3911
3912     // -----------------------------------------------------
3913     // for nodes on seam edge, compute one more UV ( uvMap2 );
3914     // find movable nodes linked to nodes on seam and which
3915     // are to be smoothed using the second UV ( uvMap2 )
3916     // -----------------------------------------------------
3917
3918     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3919     if ( !surface.IsNull() ) {
3920       TopExp_Explorer eExp( face, TopAbs_EDGE );
3921       for ( ; eExp.More(); eExp.Next() ) {
3922         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3923         if ( !BRep_Tool::IsClosed( edge, face ))
3924           continue;
3925         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3926         if ( !sm ) continue;
3927         // find out which parameter varies for a node on seam
3928         double f,l;
3929         gp_Pnt2d uv1, uv2;
3930         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3931         if ( pcurve.IsNull() ) continue;
3932         uv1 = pcurve->Value( f );
3933         edge.Reverse();
3934         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3935         if ( pcurve.IsNull() ) continue;
3936         uv2 = pcurve->Value( f );
3937         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3938         // assure uv1 < uv2
3939         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3940           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3941         }
3942         // get nodes on seam and its vertices
3943         list< const SMDS_MeshNode* > seamNodes;
3944         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3945         while ( nSeamIt->more() ) {
3946           const SMDS_MeshNode* node = nSeamIt->next();
3947           if ( !isQuadratic || !IsMedium( node ))
3948             seamNodes.push_back( node );
3949         }
3950         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3951         for ( ; vExp.More(); vExp.Next() ) {
3952           sm = aMesh->MeshElements( vExp.Current() );
3953           if ( sm ) {
3954             nSeamIt = sm->GetNodes();
3955             while ( nSeamIt->more() )
3956               seamNodes.push_back( nSeamIt->next() );
3957           }
3958         }
3959         // loop on nodes on seam
3960         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3961         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3962           const SMDS_MeshNode* nSeam = *noSeIt;
3963           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3964           if ( n_uv == uvMap.end() )
3965             continue;
3966           // set the first UV
3967           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3968           // set the second UV
3969           listUV.push_back( *n_uv->second );
3970           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3971           if ( uvMap2.empty() )
3972             uvMap2 = uvMap; // copy the uvMap contents
3973           uvMap2[ nSeam ] = &listUV.back();
3974
3975           // collect movable nodes linked to ones on seam in nodesNearSeam
3976           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3977           while ( eIt->more() ) {
3978             const SMDS_MeshElement* e = eIt->next();
3979             int nbUseMap1 = 0, nbUseMap2 = 0;
3980             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3981             int nn = 0, nbn =  e->NbNodes();
3982             if(e->IsQuadratic()) nbn = nbn/2;
3983             while ( nn++ < nbn )
3984             {
3985               const SMDS_MeshNode* n =
3986                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3987               if (n == nSeam ||
3988                   setMovableNodes.find( n ) == setMovableNodes.end() )
3989                 continue;
3990               // add only nodes being closer to uv2 than to uv1
3991               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3992                            0.5 * ( n->Y() + nSeam->Y() ),
3993                            0.5 * ( n->Z() + nSeam->Z() ));
3994               gp_XY uv;
3995               getClosestUV( projector, pMid, uv );
3996               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3997                 nodesNearSeam.insert( n );
3998                 nbUseMap2++;
3999               }
4000               else
4001                 nbUseMap1++;
4002             }
4003             // for centroidalSmooth all element nodes must
4004             // be on one side of a seam
4005             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4006               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4007               nn = 0;
4008               while ( nn++ < nbn ) {
4009                 const SMDS_MeshNode* n =
4010                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4011                 setMovableNodes.erase( n );
4012               }
4013             }
4014           }
4015         } // loop on nodes on seam
4016       } // loop on edge of a face
4017     } // if ( !face.IsNull() )
4018
4019     if ( setMovableNodes.empty() ) {
4020       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4021       continue; // goto next face
4022     }
4023
4024     // -------------
4025     // SMOOTHING //
4026     // -------------
4027
4028     int it = -1;
4029     double maxRatio = -1., maxDisplacement = -1.;
4030     set<const SMDS_MeshNode*>::iterator nodeToMove;
4031     for ( it = 0; it < theNbIterations; it++ ) {
4032       maxDisplacement = 0.;
4033       nodeToMove = setMovableNodes.begin();
4034       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4035         const SMDS_MeshNode* node = (*nodeToMove);
4036         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4037
4038         // smooth
4039         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4040         if ( theSmoothMethod == LAPLACIAN )
4041           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4042         else
4043           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4044
4045         // node displacement
4046         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4047         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4048         if ( aDispl > maxDisplacement )
4049           maxDisplacement = aDispl;
4050       }
4051       // no node movement => exit
4052       //if ( maxDisplacement < 1.e-16 ) {
4053       if ( maxDisplacement < disttol ) {
4054         MESSAGE("-- no node movement --");
4055         break;
4056       }
4057
4058       // check elements quality
4059       maxRatio  = 0;
4060       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4062         const SMDS_MeshElement* elem = (*elemIt);
4063         if ( !elem || elem->GetType() != SMDSAbs_Face )
4064           continue;
4065         SMESH::Controls::TSequenceOfXYZ aPoints;
4066         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4067           double aValue = aQualityFunc.GetValue( aPoints );
4068           if ( aValue > maxRatio )
4069             maxRatio = aValue;
4070         }
4071       }
4072       if ( maxRatio <= theTgtAspectRatio ) {
4073         MESSAGE("-- quality achived --");
4074         break;
4075       }
4076       if (it+1 == theNbIterations) {
4077         MESSAGE("-- Iteration limit exceeded --");
4078       }
4079     } // smoothing iterations
4080
4081     MESSAGE(" Face id: " << *fId <<
4082             " Nb iterstions: " << it <<
4083             " Displacement: " << maxDisplacement <<
4084             " Aspect Ratio " << maxRatio);
4085
4086     // ---------------------------------------
4087     // new nodes positions are computed,
4088     // record movement in DS and set new UV
4089     // ---------------------------------------
4090     nodeToMove = setMovableNodes.begin();
4091     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4092       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4093       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4094       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4095       if ( node_uv != uvMap.end() ) {
4096         gp_XY* uv = node_uv->second;
4097         node->SetPosition
4098           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4099       }
4100     }
4101
4102     // move medium nodes of quadratic elements
4103     if ( isQuadratic )
4104     {
4105       SMESH_MesherHelper helper( *GetMesh() );
4106       helper.SetSubShape( face );
4107       vector<const SMDS_MeshNode*> nodes;
4108       bool checkUV;
4109       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4110       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4111       {
4112         const SMDS_MeshElement* QF = *elemIt;
4113         if ( QF->IsQuadratic() )
4114         {
4115           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4116                         SMDS_MeshElement::iterator() );
4117           nodes.push_back( nodes[0] );
4118           gp_Pnt xyz;
4119           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4120           {
4121             if ( !surface.IsNull() )
4122             {
4123               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4124               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4125               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4126               xyz = surface->Value( uv.X(), uv.Y() );
4127             }
4128             else {
4129               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4130             }
4131             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4132               // we have to move a medium node
4133               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4134           }
4135         }
4136       }
4137     }
4138
4139   } // loop on face ids
4140
4141 }
4142
4143 //=======================================================================
4144 //function : isReverse
4145 //purpose  : Return true if normal of prevNodes is not co-directied with
4146 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4147 //           iNotSame is where prevNodes and nextNodes are different.
4148 //           If result is true then future volume orientation is OK
4149 //=======================================================================
4150
4151 static bool isReverse(const SMDS_MeshElement*             face,
4152                       const vector<const SMDS_MeshNode*>& prevNodes,
4153                       const vector<const SMDS_MeshNode*>& nextNodes,
4154                       const int                           iNotSame)
4155 {
4156
4157   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4158   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4159   gp_XYZ extrDir( pN - pP ), faceNorm;
4160   SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4161
4162   return faceNorm * extrDir < 0.0;
4163 }
4164
4165 //=======================================================================
4166 /*!
4167  * \brief Create elements by sweeping an element
4168  * \param elem - element to sweep
4169  * \param newNodesItVec - nodes generated from each node of the element
4170  * \param newElems - generated elements
4171  * \param nbSteps - number of sweeping steps
4172  * \param srcElements - to append elem for each generated element
4173  */
4174 //=======================================================================
4175
4176 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4177                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4178                                     list<const SMDS_MeshElement*>&        newElems,
4179                                     const int                             nbSteps,
4180                                     SMESH_SequenceOfElemPtr&              srcElements)
4181 {
4182   //MESSAGE("sweepElement " << nbSteps);
4183   SMESHDS_Mesh* aMesh = GetMeshDS();
4184
4185   const int           nbNodes = elem->NbNodes();
4186   const int         nbCorners = elem->NbCornerNodes();
4187   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4188                                                           polyhedron creation !!! */
4189   // Loop on elem nodes:
4190   // find new nodes and detect same nodes indices
4191   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4192   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4193   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4194   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4195
4196   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4197   vector<int> sames(nbNodes);
4198   vector<bool> isSingleNode(nbNodes);
4199
4200   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4202     const SMDS_MeshNode*                         node = nnIt->first;
4203     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4204     if ( listNewNodes.empty() )
4205       return;
4206
4207     itNN   [ iNode ] = listNewNodes.begin();
4208     prevNod[ iNode ] = node;
4209     nextNod[ iNode ] = listNewNodes.front();
4210
4211     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4212                                                              corner node of linear */
4213     if ( prevNod[ iNode ] != nextNod [ iNode ])
4214       nbDouble += !isSingleNode[iNode];
4215
4216     if( iNode < nbCorners ) { // check corners only
4217       if ( prevNod[ iNode ] == nextNod [ iNode ])
4218         sames[nbSame++] = iNode;
4219       else
4220         iNotSameNode = iNode;
4221     }
4222   }
4223
4224   if ( nbSame == nbNodes || nbSame > 2) {
4225     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4226     return;
4227   }
4228
4229   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4230   {
4231     // fix nodes order to have bottom normal external
4232     if ( baseType == SMDSEntity_Polygon )
4233     {
4234       std::reverse( itNN.begin(), itNN.end() );
4235       std::reverse( prevNod.begin(), prevNod.end() );
4236       std::reverse( midlNod.begin(), midlNod.end() );
4237       std::reverse( nextNod.begin(), nextNod.end() );
4238       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4239     }
4240     else
4241     {
4242       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4243       SMDS_MeshCell::applyInterlace( ind, itNN );
4244       SMDS_MeshCell::applyInterlace( ind, prevNod );
4245       SMDS_MeshCell::applyInterlace( ind, nextNod );
4246       SMDS_MeshCell::applyInterlace( ind, midlNod );
4247       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4248       if ( nbSame > 0 )
4249       {
4250         sames[nbSame] = iNotSameNode;
4251         for ( int j = 0; j <= nbSame; ++j )
4252           for ( size_t i = 0; i < ind.size(); ++i )
4253             if ( ind[i] == sames[j] )
4254             {
4255               sames[j] = i;
4256               break;
4257             }
4258         iNotSameNode = sames[nbSame];
4259       }
4260     }
4261   }
4262
4263   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4264   if ( nbSame > 0 ) {
4265     iSameNode    = sames[ nbSame-1 ];
4266     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4267     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4268     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4269   }
4270
4271   // make new elements
4272   for (int iStep = 0; iStep < nbSteps; iStep++ )
4273   {
4274     // get next nodes
4275     for ( iNode = 0; iNode < nbNodes; iNode++ )
4276     {
4277       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4278       nextNod[ iNode ] = *itNN[ iNode ]++;
4279     }
4280
4281     SMDS_MeshElement* aNewElem = 0;
4282     /*if(!elem->IsPoly())*/ {
4283       switch ( baseType ) {
4284       case SMDSEntity_0D:
4285       case SMDSEntity_Node: { // sweep NODE
4286         if ( nbSame == 0 ) {
4287           if ( isSingleNode[0] )
4288             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4289           else
4290             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4291         }
4292         else
4293           return;
4294         break;
4295       }
4296       case SMDSEntity_Edge: { // sweep EDGE
4297         if ( nbDouble == 0 )
4298         {
4299           if ( nbSame == 0 ) // ---> quadrangle
4300             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4301                                       nextNod[ 1 ], nextNod[ 0 ] );
4302           else               // ---> triangle
4303             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4304                                       nextNod[ iNotSameNode ] );
4305         }
4306         else                 // ---> polygon
4307         {
4308           vector<const SMDS_MeshNode*> poly_nodes;
4309           poly_nodes.push_back( prevNod[0] );
4310           poly_nodes.push_back( prevNod[1] );
4311           if ( prevNod[1] != nextNod[1] )
4312           {
4313             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4314             poly_nodes.push_back( nextNod[1] );
4315           }
4316           if ( prevNod[0] != nextNod[0] )
4317           {
4318             poly_nodes.push_back( nextNod[0] );
4319             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4320           }
4321           switch ( poly_nodes.size() ) {
4322           case 3:
4323             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4324             break;
4325           case 4:
4326             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4327                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4328             break;
4329           default:
4330             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4331           }
4332         }
4333         break;
4334       }
4335       case SMDSEntity_Triangle: // TRIANGLE --->
4336         {
4337           if ( nbDouble > 0 ) break;
4338           if ( nbSame == 0 )       // ---> pentahedron
4339             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4340                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4341
4342           else if ( nbSame == 1 )  // ---> pyramid
4343             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4344                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4345                                          nextNod[ iSameNode ]);
4346
4347           else // 2 same nodes:       ---> tetrahedron
4348             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4349                                          nextNod[ iNotSameNode ]);
4350           break;
4351         }
4352       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4353         {
4354           if ( nbSame == 2 )
4355             return;
4356           if ( nbDouble+nbSame == 2 )
4357           {
4358             if(nbSame==0) {      // ---> quadratic quadrangle
4359               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4360                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4361             }
4362             else { //(nbSame==1) // ---> quadratic triangle
4363               if(sames[0]==2) {
4364                 return; // medium node on axis
4365               }
4366               else if(sames[0]==0)
4367                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4368                                           nextNod[2], midlNod[1], prevNod[2]);
4369               else // sames[0]==1
4370                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4371                                           midlNod[0], nextNod[2], prevNod[2]);
4372             }
4373           }
4374           else if ( nbDouble == 3 )
4375           {
4376             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4377               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4378                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4379             }
4380           }
4381           else
4382             return;
4383           break;
4384         }
4385       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4386         if ( nbDouble > 0 ) break;
4387
4388         if ( nbSame == 0 )       // ---> hexahedron
4389           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4390                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4391
4392         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4393           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4394                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4395                                        nextNod[ iSameNode ]);
4396           newElems.push_back( aNewElem );
4397           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4398                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4399                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4400         }
4401         else if ( nbSame == 2 ) { // ---> pentahedron
4402           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4403             // iBeforeSame is same too
4404             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4405                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4406                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4407           else
4408             // iAfterSame is same too
4409             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4410                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4411                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4412         }
4413         break;
4414       }
4415       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4416       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4417         if ( nbDouble+nbSame != 3 ) break;
4418         if(nbSame==0) {
4419           // --->  pentahedron with 15 nodes
4420           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4421                                        nextNod[0], nextNod[1], nextNod[2],
4422                                        prevNod[3], prevNod[4], prevNod[5],
4423                                        nextNod[3], nextNod[4], nextNod[5],
4424                                        midlNod[0], midlNod[1], midlNod[2]);
4425         }
4426         else if(nbSame==1) {
4427           // --->  2d order pyramid of 13 nodes
4428           int apex = iSameNode;
4429           int i0 = ( apex + 1 ) % nbCorners;
4430           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4431           int i0a = apex + 3;
4432           int i1a = i1 + 3;
4433           int i01 = i0 + 3;
4434           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4435                                       nextNod[i0], nextNod[i1], prevNod[apex],
4436                                       prevNod[i01], midlNod[i0],
4437                                       nextNod[i01], midlNod[i1],
4438                                       prevNod[i1a], prevNod[i0a],
4439                                       nextNod[i0a], nextNod[i1a]);
4440         }
4441         else if(nbSame==2) {
4442           // --->  2d order tetrahedron of 10 nodes
4443           int n1 = iNotSameNode;
4444           int n2 = ( n1 + 1             ) % nbCorners;
4445           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4446           int n12 = n1 + 3;
4447           int n23 = n2 + 3;
4448           int n31 = n3 + 3;
4449           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4450                                        prevNod[n12], prevNod[n23], prevNod[n31],
4451                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4452         }
4453         break;
4454       }
4455       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4456         if( nbSame == 0 ) {
4457           if ( nbDouble != 4 ) break;
4458           // --->  hexahedron with 20 nodes
4459           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4460                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4461                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4462                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4463                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4464         }
4465         else if(nbSame==1) {
4466           // ---> pyramid + pentahedron - can not be created since it is needed
4467           // additional middle node at the center of face
4468           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4469           return;
4470         }
4471         else if( nbSame == 2 ) {
4472           if ( nbDouble != 2 ) break;
4473           // --->  2d order Pentahedron with 15 nodes
4474           int n1,n2,n4,n5;
4475           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4476             // iBeforeSame is same too
4477             n1 = iBeforeSame;
4478             n2 = iOpposSame;
4479             n4 = iSameNode;
4480             n5 = iAfterSame;
4481           }
4482           else {
4483             // iAfterSame is same too
4484             n1 = iSameNode;
4485             n2 = iBeforeSame;
4486             n4 = iAfterSame;
4487             n5 = iOpposSame;
4488           }
4489           int n12 = n2 + 4;
4490           int n45 = n4 + 4;
4491           int n14 = n1 + 4;
4492           int n25 = n5 + 4;
4493           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4494                                        prevNod[n4], prevNod[n5], nextNod[n5],
4495                                        prevNod[n12], midlNod[n2], nextNod[n12],
4496                                        prevNod[n45], midlNod[n5], nextNod[n45],
4497                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4498         }
4499         break;
4500       }
4501       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4502
4503         if( nbSame == 0 && nbDouble == 9 ) {
4504           // --->  tri-quadratic hexahedron with 27 nodes
4505           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4506                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4507                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4508                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4509                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4510                                        prevNod[8], // bottom center
4511                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4512                                        nextNod[8], // top center
4513                                        midlNod[8]);// elem center
4514         }
4515         else
4516         {
4517           return;
4518         }
4519         break;
4520       }
4521       case SMDSEntity_Polygon: { // sweep POLYGON
4522
4523         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4524           // --->  hexagonal prism
4525           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4526                                        prevNod[3], prevNod[4], prevNod[5],
4527                                        nextNod[0], nextNod[1], nextNod[2],
4528                                        nextNod[3], nextNod[4], nextNod[5]);
4529         }
4530         break;
4531       }
4532       case SMDSEntity_Ball:
4533         return;
4534
4535       default:
4536         break;
4537       }
4538     }
4539
4540     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4541     {
4542       if ( baseType != SMDSEntity_Polygon )
4543       {
4544         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4545         SMDS_MeshCell::applyInterlace( ind, prevNod );
4546         SMDS_MeshCell::applyInterlace( ind, nextNod );
4547         SMDS_MeshCell::applyInterlace( ind, midlNod );
4548         SMDS_MeshCell::applyInterlace( ind, itNN );
4549         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4550         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4551       }
4552       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4553       vector<int> quantities (nbNodes + 2);
4554       polyedre_nodes.clear();
4555       quantities.clear();
4556
4557       // bottom of prism
4558       for (int inode = 0; inode < nbNodes; inode++)
4559         polyedre_nodes.push_back( prevNod[inode] );
4560       quantities.push_back( nbNodes );
4561
4562       // top of prism
4563       polyedre_nodes.push_back( nextNod[0] );
4564       for (int inode = nbNodes; inode-1; --inode )
4565         polyedre_nodes.push_back( nextNod[inode-1] );
4566       quantities.push_back( nbNodes );
4567
4568       // side faces
4569       for (int iface = 0; iface < nbNodes; iface++)
4570       {
4571         const int prevNbNodes = polyedre_nodes.size();
4572         int inextface = (iface+1) % nbNodes;
4573         polyedre_nodes.push_back( prevNod[inextface] );
4574         polyedre_nodes.push_back( prevNod[iface] );
4575         if ( prevNod[iface] != nextNod[iface] )
4576         {
4577           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4578           polyedre_nodes.push_back( nextNod[iface] );
4579         }
4580         if ( prevNod[inextface] != nextNod[inextface] )
4581         {
4582           polyedre_nodes.push_back( nextNod[inextface] );
4583           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4584         }
4585         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4586         if ( nbFaceNodes > 2 )
4587           quantities.push_back( nbFaceNodes );
4588         else // degenerated face
4589           polyedre_nodes.resize( prevNbNodes );
4590       }
4591       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4592     }
4593
4594     if ( aNewElem ) {
4595       newElems.push_back( aNewElem );
4596       myLastCreatedElems.Append(aNewElem);
4597       srcElements.Append( elem );
4598     }
4599
4600     // set new prev nodes
4601     for ( iNode = 0; iNode < nbNodes; iNode++ )
4602       prevNod[ iNode ] = nextNod[ iNode ];
4603
4604   } // for steps
4605 }
4606
4607 //=======================================================================
4608 /*!
4609  * \brief Create 1D and 2D elements around swept elements
4610  * \param mapNewNodes - source nodes and ones generated from them
4611  * \param newElemsMap - source elements and ones generated from them
4612  * \param elemNewNodesMap - nodes generated from each node of each element
4613  * \param elemSet - all swept elements
4614  * \param nbSteps - number of sweeping steps
4615  * \param srcElements - to append elem for each generated element
4616  */
4617 //=======================================================================
4618
4619 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4620                                   TTElemOfElemListMap &    newElemsMap,
4621                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4622                                   TIDSortedElemSet&        elemSet,
4623                                   const int                nbSteps,
4624                                   SMESH_SequenceOfElemPtr& srcElements)
4625 {
4626   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4627   SMESHDS_Mesh* aMesh = GetMeshDS();
4628
4629   // Find nodes belonging to only one initial element - sweep them into edges.
4630
4631   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4632   for ( ; nList != mapNewNodes.end(); nList++ )
4633   {
4634     const SMDS_MeshNode* node =
4635       static_cast<const SMDS_MeshNode*>( nList->first );
4636     if ( newElemsMap.count( node ))
4637       continue; // node was extruded into edge
4638     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4639     int nbInitElems = 0;
4640     const SMDS_MeshElement* el = 0;
4641     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4642     while ( eIt->more() && nbInitElems < 2 ) {
4643       el = eIt->next();
4644       SMDSAbs_ElementType type = el->GetType();
4645       if ( type == SMDSAbs_Volume || type < highType ) continue;
4646       if ( type > highType ) {
4647         nbInitElems = 0;
4648         highType = type;
4649       }
4650       nbInitElems += elemSet.count(el);
4651     }
4652     if ( nbInitElems < 2 ) {
4653       bool NotCreateEdge = el && el->IsMediumNode(node);
4654       if(!NotCreateEdge) {
4655         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4656         list<const SMDS_MeshElement*> newEdges;
4657         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4658       }
4659     }
4660   }
4661
4662   // Make a ceiling for each element ie an equal element of last new nodes.
4663   // Find free links of faces - make edges and sweep them into faces.
4664
4665   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4666   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4667   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4668   {
4669     const SMDS_MeshElement* elem = itElem->first;
4670     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4671
4672     if(itElem->second.size()==0) continue;
4673
4674     const bool isQuadratic = elem->IsQuadratic();
4675
4676     if ( elem->GetType() == SMDSAbs_Edge ) {
4677       // create a ceiling edge
4678       if ( !isQuadratic ) {
4679         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4680                                vecNewNodes[ 1 ]->second.back())) {
4681           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4682                                                    vecNewNodes[ 1 ]->second.back()));
4683           srcElements.Append( elem );
4684         }
4685       }
4686       else {
4687         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4688                                vecNewNodes[ 1 ]->second.back(),
4689                                vecNewNodes[ 2 ]->second.back())) {
4690           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4691                                                    vecNewNodes[ 1 ]->second.back(),
4692                                                    vecNewNodes[ 2 ]->second.back()));
4693           srcElements.Append( elem );
4694         }
4695       }
4696     }
4697     if ( elem->GetType() != SMDSAbs_Face )
4698       continue;
4699
4700     bool hasFreeLinks = false;
4701
4702     TIDSortedElemSet avoidSet;
4703     avoidSet.insert( elem );
4704
4705     set<const SMDS_MeshNode*> aFaceLastNodes;
4706     int iNode, nbNodes = vecNewNodes.size();
4707     if ( !isQuadratic ) {
4708       // loop on the face nodes
4709       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4710         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4711         // look for free links of the face
4712         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4713         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4714         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4715         // check if a link n1-n2 is free
4716         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4717           hasFreeLinks = true;
4718           // make a new edge and a ceiling for a new edge
4719           const SMDS_MeshElement* edge;
4720           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4721             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4722             srcElements.Append( myLastCreatedElems.Last() );
4723           }
4724           n1 = vecNewNodes[ iNode ]->second.back();
4725           n2 = vecNewNodes[ iNext ]->second.back();
4726           if ( !aMesh->FindEdge( n1, n2 )) {
4727             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4728             srcElements.Append( edge );
4729           }
4730         }
4731       }
4732     }
4733     else { // elem is quadratic face
4734       int nbn = nbNodes/2;
4735       for ( iNode = 0; iNode < nbn; iNode++ ) {
4736         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4737         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4738         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4739         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4740         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4741         // check if a link is free
4742         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4743              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4744              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4745           hasFreeLinks = true;
4746           // make an edge and a ceiling for a new edge
4747           // find medium node
4748           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4749             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4750             srcElements.Append( elem );
4751           }
4752           n1 = vecNewNodes[ iNode ]->second.back();
4753           n2 = vecNewNodes[ iNext ]->second.back();
4754           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4755           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4756             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4757             srcElements.Append( elem );
4758           }
4759         }
4760       }
4761       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4762         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4763       }
4764     }
4765
4766     // sweep free links into faces
4767
4768     if ( hasFreeLinks )  {
4769       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4770       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4771
4772       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4773       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4774       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4775         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4776         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4777       }
4778       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4779         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4780         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4781       }
4782       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4783         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4784         std::advance( v, volNb );
4785         // find indices of free faces of a volume and their source edges
4786         list< int > freeInd;
4787         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4788         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4789         int iF, nbF = vTool.NbFaces();
4790         for ( iF = 0; iF < nbF; iF ++ ) {
4791           if (vTool.IsFreeFace( iF ) &&
4792               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4793               initNodeSet != faceNodeSet) // except an initial face
4794           {
4795             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4796               continue;
4797             if ( faceNodeSet == initNodeSetNoCenter )
4798               continue;
4799             freeInd.push_back( iF );
4800             // find source edge of a free face iF
4801             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4802             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4803             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4804                                    initNodeSet.begin(), initNodeSet.end(),
4805                                    commonNodes.begin());
4806             if ( (*v)->IsQuadratic() )
4807               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4808             else
4809               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4810 #ifdef _DEBUG_
4811             if ( !srcEdges.back() )
4812             {
4813               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4814                    << iF << " of volume #" << vTool.ID() << endl;
4815             }
4816 #endif
4817           }
4818         }
4819         if ( freeInd.empty() )
4820           continue;
4821
4822         // create faces for all steps;
4823         // if such a face has been already created by sweep of edge,
4824         // assure that its orientation is OK
4825         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4826           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4827           vTool.SetExternalNormal();
4828           const int nextShift = vTool.IsForward() ? +1 : -1;
4829           list< int >::iterator ind = freeInd.begin();
4830           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4831           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4832           {
4833             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4834             int nbn = vTool.NbFaceNodes( *ind );
4835             const SMDS_MeshElement * f = 0;
4836             if ( nbn == 3 )              ///// triangle
4837             {
4838               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4839               if ( !f ||
4840                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4841               {
4842                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4843                                                      nodes[ 1 ],
4844                                                      nodes[ 1 + nextShift ] };
4845                 if ( f )
4846                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4847                 else
4848                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4849                                                             newOrder[ 2 ] ));
4850               }
4851             }
4852             else if ( nbn == 4 )       ///// quadrangle
4853             {
4854               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4855               if ( !f ||
4856                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4857               {
4858                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4859                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4860                 if ( f )
4861                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4862                 else
4863                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4864                                                             newOrder[ 2 ], newOrder[ 3 ]));
4865               }
4866             }
4867             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4868             {
4869               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4870               if ( !f ||
4871                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4872               {
4873                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4874                                                      nodes[2],
4875                                                      nodes[2 + 2*nextShift],
4876                                                      nodes[3 - 2*nextShift],
4877                                                      nodes[3],
4878                                                      nodes[3 + 2*nextShift]};
4879                 if ( f )
4880                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4881                 else
4882                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4883                                                             newOrder[ 1 ],
4884                                                             newOrder[ 2 ],
4885                                                             newOrder[ 3 ],
4886                                                             newOrder[ 4 ],
4887                                                             newOrder[ 5 ] ));
4888               }
4889             }
4890             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4891             {
4892               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4893                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4894               if ( !f ||
4895                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4896               {
4897                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4898                                                      nodes[4 - 2*nextShift],
4899                                                      nodes[4],
4900                                                      nodes[4 + 2*nextShift],
4901                                                      nodes[1],
4902                                                      nodes[5 - 2*nextShift],
4903                                                      nodes[5],
4904                                                      nodes[5 + 2*nextShift] };
4905                 if ( f )
4906                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4907                 else
4908                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4909                                                            newOrder[ 2 ], newOrder[ 3 ],
4910                                                            newOrder[ 4 ], newOrder[ 5 ],
4911                                                            newOrder[ 6 ], newOrder[ 7 ]));
4912               }
4913             }
4914             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4915             {
4916               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4917                                       SMDSAbs_Face, /*noMedium=*/false);
4918               if ( !f ||
4919                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4920               {
4921                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4922                                                      nodes[4 - 2*nextShift],
4923                                                      nodes[4],
4924                                                      nodes[4 + 2*nextShift],
4925                                                      nodes[1],
4926                                                      nodes[5 - 2*nextShift],
4927                                                      nodes[5],
4928                                                      nodes[5 + 2*nextShift],
4929                                                      nodes[8] };
4930                 if ( f )
4931                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4932                 else
4933                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4934                                                            newOrder[ 2 ], newOrder[ 3 ],
4935                                                            newOrder[ 4 ], newOrder[ 5 ],
4936                                                            newOrder[ 6 ], newOrder[ 7 ],
4937                                                            newOrder[ 8 ]));
4938               }
4939             }
4940             else  //////// polygon
4941             {
4942               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4943               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4944               if ( !f ||
4945                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4946               {
4947                 if ( !vTool.IsForward() )
4948                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4949                 if ( f )
4950                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4951                 else
4952                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4953               }
4954             }
4955
4956             while ( srcElements.Length() < myLastCreatedElems.Length() )
4957               srcElements.Append( *srcEdge );
4958
4959           }  // loop on free faces
4960
4961           // go to the next volume
4962           iVol = 0;
4963           while ( iVol++ < nbVolumesByStep ) v++;
4964
4965         } // loop on steps
4966       } // loop on volumes of one step
4967     } // sweep free links into faces
4968
4969     // Make a ceiling face with a normal external to a volume
4970
4971     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4972     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4973     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4974
4975     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4976       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4977       iF = lastVol.GetFaceIndex( aFaceLastNodes );
4978     }
4979     if ( iF >= 0 ) {
4980       lastVol.SetExternalNormal();
4981       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4982       int nbn = lastVol.NbFaceNodes( iF );
4983       // we do not use this->AddElement() because nodes are interlaced
4984       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4985       if ( !hasFreeLinks ||
4986            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4987       {
4988         if ( nbn == 3 )
4989           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4990
4991         else if ( nbn == 4 )
4992           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4993
4994         else if ( nbn == 6 && isQuadratic )
4995           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4996                                                     nodes[1], nodes[3], nodes[5]));
4997         else if ( nbn == 7 && isQuadratic )
4998           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4999                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
5000         else if ( nbn == 8 && isQuadratic )
5001           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5002                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5003         else if ( nbn == 9 && isQuadratic )
5004           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5005                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5006                                                     nodes[8]));
5007         else
5008           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5009
5010         while ( srcElements.Length() < myLastCreatedElems.Length() )
5011           srcElements.Append( elem );
5012       }
5013     }
5014   } // loop on swept elements
5015 }
5016
5017 //=======================================================================
5018 //function : RotationSweep
5019 //purpose  :
5020 //=======================================================================
5021
5022 SMESH_MeshEditor::PGroupIDs
5023 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5024                                 const gp_Ax1&      theAxis,
5025                                 const double       theAngle,
5026                                 const int          theNbSteps,
5027                                 const double       theTol,
5028                                 const bool         theMakeGroups,
5029                                 const bool         theMakeWalls)
5030 {
5031   myLastCreatedElems.Clear();
5032   myLastCreatedNodes.Clear();
5033
5034   // source elements for each generated one
5035   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5036
5037   MESSAGE( "RotationSweep()");
5038   gp_Trsf aTrsf;
5039   aTrsf.SetRotation( theAxis, theAngle );
5040   gp_Trsf aTrsf2;
5041   aTrsf2.SetRotation( theAxis, theAngle/2. );
5042
5043   gp_Lin aLine( theAxis );
5044   double aSqTol = theTol * theTol;
5045
5046   SMESHDS_Mesh* aMesh = GetMeshDS();
5047
5048   TNodeOfNodeListMap mapNewNodes;
5049   TElemOfVecOfNnlmiMap mapElemNewNodes;
5050   TTElemOfElemListMap newElemsMap;
5051
5052   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5053                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5054                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5055   // loop on theElems
5056   TIDSortedElemSet::iterator itElem;
5057   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5058     const SMDS_MeshElement* elem = *itElem;
5059     if ( !elem || elem->GetType() == SMDSAbs_Volume )
5060       continue;
5061     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5062     newNodesItVec.reserve( elem->NbNodes() );
5063
5064     // loop on elem nodes
5065     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5066     while ( itN->more() )
5067     {
5068       // check if a node has been already sweeped
5069       const SMDS_MeshNode* node = cast2Node( itN->next() );
5070
5071       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5072       double coord[3];
5073       aXYZ.Coord( coord[0], coord[1], coord[2] );
5074       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5075
5076       TNodeOfNodeListMapItr nIt =
5077         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5078       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5079       if ( listNewNodes.empty() )
5080       {
5081         // check if we are to create medium nodes between corner ones
5082         bool needMediumNodes = false;
5083         if ( isQuadraticMesh )
5084         {
5085           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5086           while (it->more() && !needMediumNodes )
5087           {
5088             const SMDS_MeshElement* invElem = it->next();
5089             if ( invElem != elem && !theElems.count( invElem )) continue;
5090             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5091             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5092               needMediumNodes = true;
5093           }
5094         }
5095
5096         // make new nodes
5097         const SMDS_MeshNode * newNode = node;
5098         for ( int i = 0; i < theNbSteps; i++ ) {
5099           if ( !isOnAxis ) {
5100             if ( needMediumNodes )  // create a medium node
5101             {
5102               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5103               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5104               myLastCreatedNodes.Append(newNode);
5105               srcNodes.Append( node );
5106               listNewNodes.push_back( newNode );
5107               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5108             }
5109             else {
5110               aTrsf.Transforms( coord[0], coord[1], coord[2] );
5111             }
5112             // create a corner node
5113             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5114             myLastCreatedNodes.Append(newNode);
5115             srcNodes.Append( node );
5116             listNewNodes.push_back( newNode );
5117           }
5118           else {
5119             listNewNodes.push_back( newNode );
5120             // if ( needMediumNodes )
5121             //   listNewNodes.push_back( newNode );
5122           }
5123         }
5124       }
5125       newNodesItVec.push_back( nIt );
5126     }
5127     // make new elements
5128     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5129   }
5130
5131   if ( theMakeWalls )
5132     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5133
5134   PGroupIDs newGroupIDs;
5135   if ( theMakeGroups )
5136     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5137
5138   return newGroupIDs;
5139 }
5140
5141
5142 //=======================================================================
5143 //function : CreateNode
5144 //purpose  :
5145 //=======================================================================
5146 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5147                                                   const double y,
5148                                                   const double z,
5149                                                   const double tolnode,
5150                                                   SMESH_SequenceOfNode& aNodes)
5151 {
5152   // myLastCreatedElems.Clear();
5153   // myLastCreatedNodes.Clear();
5154
5155   gp_Pnt P1(x,y,z);
5156   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5157
5158   // try to search in sequence of existing nodes
5159   // if aNodes.Length()>0 we 'nave to use given sequence
5160   // else - use all nodes of mesh
5161   if(aNodes.Length()>0) {
5162     int i;
5163     for(i=1; i<=aNodes.Length(); i++) {
5164       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5165       if(P1.Distance(P2)<tolnode)
5166         return aNodes.Value(i);
5167     }
5168   }
5169   else {
5170     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5171     while(itn->more()) {
5172       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5173       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5174       if(P1.Distance(P2)<tolnode)
5175         return aN;
5176     }
5177   }
5178
5179   // create new node and return it
5180   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5181   //myLastCreatedNodes.Append(NewNode);
5182   return NewNode;
5183 }
5184
5185
5186 //=======================================================================
5187 //function : ExtrusionSweep
5188 //purpose  :
5189 //=======================================================================
5190
5191 SMESH_MeshEditor::PGroupIDs
5192 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5193                                   const gp_Vec&        theStep,
5194                                   const int            theNbSteps,
5195                                   TTElemOfElemListMap& newElemsMap,
5196                                   const bool           theMakeGroups,
5197                                   const int            theFlags,
5198                                   const double         theTolerance)
5199 {
5200   ExtrusParam aParams;
5201   aParams.myDir = gp_Dir(theStep);
5202   aParams.myNodes.Clear();
5203   aParams.mySteps = new TColStd_HSequenceOfReal;
5204   int i;
5205   for(i=1; i<=theNbSteps; i++)
5206     aParams.mySteps->Append(theStep.Magnitude());
5207
5208   return
5209     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5210 }
5211
5212
5213 //=======================================================================
5214 //function : ExtrusionSweep
5215 //purpose  :
5216 //=======================================================================
5217
5218 SMESH_MeshEditor::PGroupIDs
5219 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5220                                   ExtrusParam&         theParams,
5221                                   TTElemOfElemListMap& newElemsMap,
5222                                   const bool           theMakeGroups,
5223                                   const int            theFlags,
5224                                   const double         theTolerance)
5225 {
5226   myLastCreatedElems.Clear();
5227   myLastCreatedNodes.Clear();
5228
5229   // source elements for each generated one
5230   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5231
5232   SMESHDS_Mesh* aMesh = GetMeshDS();
5233
5234   int nbsteps = theParams.mySteps->Length();
5235
5236   TNodeOfNodeListMap mapNewNodes;
5237   //TNodeOfNodeVecMap mapNewNodes;
5238   TElemOfVecOfNnlmiMap mapElemNewNodes;
5239   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5240
5241   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5242                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5243                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5244   // loop on theElems
5245   TIDSortedElemSet::iterator itElem;
5246   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5247     // check element type
5248     const SMDS_MeshElement* elem = *itElem;
5249     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5250       continue;
5251
5252     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5253     newNodesItVec.reserve( elem->NbNodes() );
5254
5255     // loop on elem nodes
5256     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5257     while ( itN->more() )
5258     {
5259       // check if a node has been already sweeped
5260       const SMDS_MeshNode* node = cast2Node( itN->next() );
5261       TNodeOfNodeListMap::iterator nIt =
5262         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5263       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5264       if ( listNewNodes.empty() )
5265       {
5266         // make new nodes
5267
5268         // check if we are to create medium nodes between corner ones
5269         bool needMediumNodes = false;
5270         if ( isQuadraticMesh )
5271         {
5272           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5273           while (it->more() && !needMediumNodes )
5274           {
5275             const SMDS_MeshElement* invElem = it->next();
5276             if ( invElem != elem && !theElems.count( invElem )) continue;
5277             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5278             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5279               needMediumNodes = true;
5280           }
5281         }
5282
5283         double coord[] = { node->X(), node->Y(), node->Z() };
5284         for ( int i = 0; i < nbsteps; i++ )
5285         {
5286           if ( needMediumNodes ) // create a medium node
5287           {
5288             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5289             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5290             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5291             if( theFlags & EXTRUSION_FLAG_SEW ) {
5292               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5293                                                          theTolerance, theParams.myNodes);
5294               listNewNodes.push_back( newNode );
5295             }
5296             else {
5297               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5298               myLastCreatedNodes.Append(newNode);
5299               srcNodes.Append( node );
5300               listNewNodes.push_back( newNode );
5301             }
5302           }
5303           // create a corner node
5304           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5305           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5306           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5307           if( theFlags & EXTRUSION_FLAG_SEW ) {
5308             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5309                                                        theTolerance, theParams.myNodes);
5310             listNewNodes.push_back( newNode );
5311           }
5312           else {
5313             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5314             myLastCreatedNodes.Append(newNode);
5315             srcNodes.Append( node );
5316             listNewNodes.push_back( newNode );
5317           }
5318         }
5319       }
5320       newNodesItVec.push_back( nIt );
5321     }
5322     // make new elements
5323     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5324   }
5325
5326   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5327     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5328   }
5329   PGroupIDs newGroupIDs;
5330   if ( theMakeGroups )
5331     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5332
5333   return newGroupIDs;
5334 }
5335
5336 //=======================================================================
5337 //function : ExtrusionAlongTrack
5338 //purpose  :
5339 //=======================================================================
5340 SMESH_MeshEditor::Extrusion_Error
5341 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5342                                        SMESH_subMesh*       theTrack,
5343                                        const SMDS_MeshNode* theN1,
5344                                        const bool           theHasAngles,
5345                                        list<double>&        theAngles,
5346                                        const bool           theLinearVariation,
5347                                        const bool           theHasRefPoint,
5348                                        const gp_Pnt&        theRefPoint,
5349                                        const bool           theMakeGroups)
5350 {
5351   MESSAGE("ExtrusionAlongTrack");
5352   myLastCreatedElems.Clear();
5353   myLastCreatedNodes.Clear();
5354
5355   int aNbE;
5356   std::list<double> aPrms;
5357   TIDSortedElemSet::iterator itElem;
5358
5359   gp_XYZ aGC;
5360   TopoDS_Edge aTrackEdge;
5361   TopoDS_Vertex aV1, aV2;
5362
5363   SMDS_ElemIteratorPtr aItE;
5364   SMDS_NodeIteratorPtr aItN;
5365   SMDSAbs_ElementType aTypeE;
5366
5367   TNodeOfNodeListMap mapNewNodes;
5368
5369   // 1. Check data
5370   aNbE = theElements.size();
5371   // nothing to do
5372   if ( !aNbE )
5373     return EXTR_NO_ELEMENTS;
5374
5375   // 1.1 Track Pattern
5376   ASSERT( theTrack );
5377
5378   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5379
5380   aItE = pSubMeshDS->GetElements();
5381   while ( aItE->more() ) {
5382     const SMDS_MeshElement* pE = aItE->next();
5383     aTypeE = pE->GetType();
5384     // Pattern must contain links only
5385     if ( aTypeE != SMDSAbs_Edge )
5386       return EXTR_PATH_NOT_EDGE;
5387   }
5388
5389   list<SMESH_MeshEditor_PathPoint> fullList;
5390
5391   const TopoDS_Shape& aS = theTrack->GetSubShape();
5392   // Sub-shape for the Pattern must be an Edge or Wire
5393   if( aS.ShapeType() == TopAbs_EDGE ) {
5394     aTrackEdge = TopoDS::Edge( aS );
5395     // the Edge must not be degenerated
5396     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5397       return EXTR_BAD_PATH_SHAPE;
5398     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5399     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5400     const SMDS_MeshNode* aN1 = aItN->next();
5401     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5402     const SMDS_MeshNode* aN2 = aItN->next();
5403     // starting node must be aN1 or aN2
5404     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5405       return EXTR_BAD_STARTING_NODE;
5406     aItN = pSubMeshDS->GetNodes();
5407     while ( aItN->more() ) {
5408       const SMDS_MeshNode* pNode = aItN->next();
5409       const SMDS_EdgePosition* pEPos =
5410         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5411       double aT = pEPos->GetUParameter();
5412       aPrms.push_back( aT );
5413     }
5414     //Extrusion_Error err =
5415     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5416   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5417     list< SMESH_subMesh* > LSM;
5418     TopTools_SequenceOfShape Edges;
5419     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5420     while(itSM->more()) {
5421       SMESH_subMesh* SM = itSM->next();
5422       LSM.push_back(SM);
5423       const TopoDS_Shape& aS = SM->GetSubShape();
5424       Edges.Append(aS);
5425     }
5426     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5427     int startNid = theN1->GetID();
5428     TColStd_MapOfInteger UsedNums;
5429
5430     int NbEdges = Edges.Length();
5431     int i = 1;
5432     for(; i<=NbEdges; i++) {
5433       int k = 0;
5434       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5435       for(; itLSM!=LSM.end(); itLSM++) {
5436         k++;
5437         if(UsedNums.Contains(k)) continue;
5438         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5439         SMESH_subMesh* locTrack = *itLSM;
5440         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5441         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5442         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5443         const SMDS_MeshNode* aN1 = aItN->next();
5444         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5445         const SMDS_MeshNode* aN2 = aItN->next();
5446         // starting node must be aN1 or aN2
5447         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5448         // 2. Collect parameters on the track edge
5449         aPrms.clear();
5450         aItN = locMeshDS->GetNodes();
5451         while ( aItN->more() ) {
5452           const SMDS_MeshNode* pNode = aItN->next();
5453           const SMDS_EdgePosition* pEPos =
5454             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5455           double aT = pEPos->GetUParameter();
5456           aPrms.push_back( aT );
5457         }
5458         list<SMESH_MeshEditor_PathPoint> LPP;
5459         //Extrusion_Error err =
5460         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5461         LLPPs.push_back(LPP);
5462         UsedNums.Add(k);
5463         // update startN for search following egde
5464         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5465         else startNid = aN1->GetID();
5466         break;
5467       }
5468     }
5469     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5470     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5471     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5472     for(; itPP!=firstList.end(); itPP++) {
5473       fullList.push_back( *itPP );
5474     }
5475     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5476     fullList.pop_back();
5477     itLLPP++;
5478     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5479       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5480       itPP = currList.begin();
5481       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5482       gp_Dir D1 = PP1.Tangent();
5483       gp_Dir D2 = PP2.Tangent();
5484       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5485                            (D1.Z()+D2.Z())/2 ) );
5486       PP1.SetTangent(Dnew);
5487       fullList.push_back(PP1);
5488       itPP++;
5489       for(; itPP!=firstList.end(); itPP++) {
5490         fullList.push_back( *itPP );
5491       }
5492       PP1 = fullList.back();
5493       fullList.pop_back();
5494     }
5495     // if wire not closed
5496     fullList.push_back(PP1);
5497     // else ???
5498   }
5499   else {
5500     return EXTR_BAD_PATH_SHAPE;
5501   }
5502
5503   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5504                           theHasRefPoint, theRefPoint, theMakeGroups);
5505 }
5506
5507
5508 //=======================================================================
5509 //function : ExtrusionAlongTrack
5510 //purpose  :
5511 //=======================================================================
5512 SMESH_MeshEditor::Extrusion_Error
5513 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5514                                        SMESH_Mesh*          theTrack,
5515                                        const SMDS_MeshNode* theN1,
5516                                        const bool           theHasAngles,
5517                                        list<double>&        theAngles,
5518                                        const bool           theLinearVariation,
5519                                        const bool           theHasRefPoint,
5520                                        const gp_Pnt&        theRefPoint,
5521                                        const bool           theMakeGroups)
5522 {
5523   myLastCreatedElems.Clear();
5524   myLastCreatedNodes.Clear();
5525
5526   int aNbE;
5527   std::list<double> aPrms;
5528   TIDSortedElemSet::iterator itElem;
5529
5530   gp_XYZ aGC;
5531   TopoDS_Edge aTrackEdge;
5532   TopoDS_Vertex aV1, aV2;
5533
5534   SMDS_ElemIteratorPtr aItE;
5535   SMDS_NodeIteratorPtr aItN;
5536   SMDSAbs_ElementType aTypeE;
5537
5538   TNodeOfNodeListMap mapNewNodes;
5539
5540   // 1. Check data
5541   aNbE = theElements.size();
5542   // nothing to do
5543   if ( !aNbE )
5544     return EXTR_NO_ELEMENTS;
5545
5546   // 1.1 Track Pattern
5547   ASSERT( theTrack );
5548
5549   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5550
5551   aItE = pMeshDS->elementsIterator();
5552   while ( aItE->more() ) {
5553     const SMDS_MeshElement* pE = aItE->next();
5554     aTypeE = pE->GetType();
5555     // Pattern must contain links only
5556     if ( aTypeE != SMDSAbs_Edge )
5557       return EXTR_PATH_NOT_EDGE;
5558   }
5559
5560   list<SMESH_MeshEditor_PathPoint> fullList;
5561
5562   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5563
5564   if ( !theTrack->HasShapeToMesh() ) {
5565     //Mesh without shape
5566     const SMDS_MeshNode* currentNode = NULL;
5567     const SMDS_MeshNode* prevNode = theN1;
5568     std::vector<const SMDS_MeshNode*> aNodesList;
5569     aNodesList.push_back(theN1);
5570     int nbEdges = 0, conn=0;
5571     const SMDS_MeshElement* prevElem = NULL;
5572     const SMDS_MeshElement* currentElem = NULL;
5573     int totalNbEdges = theTrack->NbEdges();
5574     SMDS_ElemIteratorPtr nIt;
5575
5576     //check start node
5577     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5578       return EXTR_BAD_STARTING_NODE;
5579     }
5580
5581     conn = nbEdgeConnectivity(theN1);
5582     if(conn > 2)
5583       return EXTR_PATH_NOT_EDGE;
5584
5585     aItE = theN1->GetInverseElementIterator();
5586     prevElem = aItE->next();
5587     currentElem = prevElem;
5588     //Get all nodes
5589     if(totalNbEdges == 1 ) {
5590       nIt = currentElem->nodesIterator();
5591       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5592       if(currentNode == prevNode)
5593         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5594       aNodesList.push_back(currentNode);
5595     } else {
5596       nIt = currentElem->nodesIterator();
5597       while( nIt->more() ) {
5598         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5599         if(currentNode == prevNode)
5600           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5601         aNodesList.push_back(currentNode);
5602
5603         //case of the closed mesh
5604         if(currentNode == theN1) {
5605           nbEdges++;
5606           break;
5607         }
5608
5609         conn = nbEdgeConnectivity(currentNode);
5610         if(conn > 2) {
5611           return EXTR_PATH_NOT_EDGE;
5612         }else if( conn == 1 && nbEdges > 0 ) {
5613           //End of the path
5614           nbEdges++;
5615           break;
5616         }else {
5617           prevNode = currentNode;
5618           aItE = currentNode->GetInverseElementIterator();
5619           currentElem = aItE->next();
5620           if( currentElem  == prevElem)
5621             currentElem = aItE->next();
5622           nIt = currentElem->nodesIterator();
5623           prevElem = currentElem;
5624           nbEdges++;
5625         }
5626       }
5627     }
5628
5629     if(nbEdges != totalNbEdges)
5630       return EXTR_PATH_NOT_EDGE;
5631
5632     TopTools_SequenceOfShape Edges;
5633     double x1,x2,y1,y2,z1,z2;
5634     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5635     int startNid = theN1->GetID();
5636     for(int i = 1; i < aNodesList.size(); i++) {
5637       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5638       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5639       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5640       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5641       list<SMESH_MeshEditor_PathPoint> LPP;
5642       aPrms.clear();
5643       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5644       LLPPs.push_back(LPP);
5645       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5646       else startNid = aNodesList[i-1]->GetID();
5647
5648     }
5649
5650     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5651     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5652     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5653     for(; itPP!=firstList.end(); itPP++) {
5654       fullList.push_back( *itPP );
5655     }
5656
5657     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5658     SMESH_MeshEditor_PathPoint PP2;
5659     fullList.pop_back();
5660     itLLPP++;
5661     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5662       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5663       itPP = currList.begin();
5664       PP2 = currList.front();
5665       gp_Dir D1 = PP1.Tangent();
5666       gp_Dir D2 = PP2.Tangent();
5667       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5668                            (D1.Z()+D2.Z())/2 ) );
5669       PP1.SetTangent(Dnew);
5670       fullList.push_back(PP1);
5671       itPP++;
5672       for(; itPP!=currList.end(); itPP++) {
5673         fullList.push_back( *itPP );
5674       }
5675       PP1 = fullList.back();
5676       fullList.pop_back();
5677     }
5678     fullList.push_back(PP1);
5679
5680   } // Sub-shape for the Pattern must be an Edge or Wire
5681   else if( aS.ShapeType() == TopAbs_EDGE ) {
5682     aTrackEdge = TopoDS::Edge( aS );
5683     // the Edge must not be degenerated
5684     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5685       return EXTR_BAD_PATH_SHAPE;
5686     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5687     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5688     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5689     // starting node must be aN1 or aN2
5690     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5691       return EXTR_BAD_STARTING_NODE;
5692     aItN = pMeshDS->nodesIterator();
5693     while ( aItN->more() ) {
5694       const SMDS_MeshNode* pNode = aItN->next();
5695       if( pNode==aN1 || pNode==aN2 ) continue;
5696       const SMDS_EdgePosition* pEPos =
5697         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5698       double aT = pEPos->GetUParameter();
5699       aPrms.push_back( aT );
5700     }
5701     //Extrusion_Error err =
5702     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5703   }
5704   else if( aS.ShapeType() == TopAbs_WIRE ) {
5705     list< SMESH_subMesh* > LSM;
5706     TopTools_SequenceOfShape Edges;
5707     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5708     for(; eExp.More(); eExp.Next()) {
5709       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5710       if( SMESH_Algo::isDegenerated(E) ) continue;
5711       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5712       if(SM) {
5713         LSM.push_back(SM);
5714         Edges.Append(E);
5715       }
5716     }
5717     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5718     TopoDS_Vertex aVprev;
5719     TColStd_MapOfInteger UsedNums;
5720     int NbEdges = Edges.Length();
5721     int i = 1;
5722     for(; i<=NbEdges; i++) {
5723       int k = 0;
5724       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5725       for(; itLSM!=LSM.end(); itLSM++) {
5726         k++;
5727         if(UsedNums.Contains(k)) continue;
5728         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5729         SMESH_subMesh* locTrack = *itLSM;
5730         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5731         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5732         bool aN1isOK = false, aN2isOK = false;
5733         if ( aVprev.IsNull() ) {
5734           // if previous vertex is not yet defined, it means that we in the beginning of wire
5735           // and we have to find initial vertex corresponding to starting node theN1
5736           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5737           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5738           // starting node must be aN1 or aN2
5739           aN1isOK = ( aN1 && aN1 == theN1 );
5740           aN2isOK = ( aN2 && aN2 == theN1 );
5741         }
5742         else {
5743           // we have specified ending vertex of the previous edge on the previous iteration
5744           // and we have just to check that it corresponds to any vertex in current segment
5745           aN1isOK = aVprev.IsSame( aV1 );
5746           aN2isOK = aVprev.IsSame( aV2 );
5747         }
5748         if ( !aN1isOK && !aN2isOK ) continue;
5749         // 2. Collect parameters on the track edge
5750         aPrms.clear();
5751         aItN = locMeshDS->GetNodes();
5752         while ( aItN->more() ) {
5753           const SMDS_MeshNode*     pNode = aItN->next();
5754           const SMDS_EdgePosition* pEPos =
5755             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5756           double aT = pEPos->GetUParameter();
5757           aPrms.push_back( aT );
5758         }
5759         list<SMESH_MeshEditor_PathPoint> LPP;
5760         //Extrusion_Error err =
5761         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5762         LLPPs.push_back(LPP);
5763         UsedNums.Add(k);
5764         // update startN for search following egde
5765         if ( aN1isOK ) aVprev = aV2;
5766         else           aVprev = aV1;
5767         break;
5768       }
5769     }
5770     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5771     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5772     fullList.splice( fullList.end(), firstList );
5773
5774     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5775     fullList.pop_back();
5776     itLLPP++;
5777     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5778       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5779       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5780       gp_Dir D1 = PP1.Tangent();
5781       gp_Dir D2 = PP2.Tangent();
5782       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5783       PP1.SetTangent(Dnew);
5784       fullList.push_back(PP1);
5785       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5786       PP1 = fullList.back();
5787       fullList.pop_back();
5788     }
5789     // if wire not closed
5790     fullList.push_back(PP1);
5791     // else ???
5792   }
5793   else {
5794     return EXTR_BAD_PATH_SHAPE;
5795   }
5796
5797   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5798                           theHasRefPoint, theRefPoint, theMakeGroups);
5799 }
5800
5801
5802 //=======================================================================
5803 //function : MakeEdgePathPoints
5804 //purpose  : auxilary for ExtrusionAlongTrack
5805 //=======================================================================
5806 SMESH_MeshEditor::Extrusion_Error
5807 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
5808                                      const TopoDS_Edge&                aTrackEdge,
5809                                      bool                              FirstIsStart,
5810                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5811 {
5812   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5813   aTolVec=1.e-7;
5814   aTolVec2=aTolVec*aTolVec;
5815   double aT1, aT2;
5816   TopoDS_Vertex aV1, aV2;
5817   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5818   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5819   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5820   // 2. Collect parameters on the track edge
5821   aPrms.push_front( aT1 );
5822   aPrms.push_back( aT2 );
5823   // sort parameters
5824   aPrms.sort();
5825   if( FirstIsStart ) {
5826     if ( aT1 > aT2 ) {
5827       aPrms.reverse();
5828     }
5829   }
5830   else {
5831     if ( aT2 > aT1 ) {
5832       aPrms.reverse();
5833     }
5834   }
5835   // 3. Path Points
5836   SMESH_MeshEditor_PathPoint aPP;
5837   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5838   std::list<double>::iterator aItD = aPrms.begin();
5839   for(; aItD != aPrms.end(); ++aItD) {
5840     double aT = *aItD;
5841     gp_Pnt aP3D;
5842     gp_Vec aVec;
5843     aC3D->D1( aT, aP3D, aVec );
5844     aL2 = aVec.SquareMagnitude();
5845     if ( aL2 < aTolVec2 )
5846       return EXTR_CANT_GET_TANGENT;
5847     gp_Dir aTgt( aVec );
5848     aPP.SetPnt( aP3D );
5849     aPP.SetTangent( aTgt );
5850     aPP.SetParameter( aT );
5851     LPP.push_back(aPP);
5852   }
5853   return EXTR_OK;
5854 }
5855
5856
5857 //=======================================================================
5858 //function : MakeExtrElements
5859 //purpose  : auxilary for ExtrusionAlongTrack
5860 //=======================================================================
5861 SMESH_MeshEditor::Extrusion_Error
5862 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&                 theElements,
5863                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5864                                    const bool                        theHasAngles,
5865                                    list<double>&                     theAngles,
5866                                    const bool                        theLinearVariation,
5867                                    const bool                        theHasRefPoint,
5868                                    const gp_Pnt&                     theRefPoint,
5869                                    const bool                        theMakeGroups)
5870 {
5871   const int aNbTP = fullList.size();
5872   // Angles
5873   if( theHasAngles && !theAngles.empty() && theLinearVariation )
5874     LinearAngleVariation(aNbTP-1, theAngles);
5875   // fill vector of path points with angles
5876   vector<SMESH_MeshEditor_PathPoint> aPPs;
5877   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5878   list<double>::iterator                 itAngles = theAngles.begin();
5879   aPPs.push_back( *itPP++ );
5880   for( ; itPP != fullList.end(); itPP++) {
5881     aPPs.push_back( *itPP );
5882     if ( theHasAngles && itAngles != theAngles.end() )
5883       aPPs.back().SetAngle( *itAngles++ );
5884   }
5885
5886   TNodeOfNodeListMap   mapNewNodes;
5887   TElemOfVecOfNnlmiMap mapElemNewNodes;
5888   TTElemOfElemListMap  newElemsMap;
5889   TIDSortedElemSet::iterator itElem;
5890   // source elements for each generated one
5891   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5892
5893   // 3. Center of rotation aV0
5894   gp_Pnt aV0 = theRefPoint;
5895   if ( !theHasRefPoint )
5896   {
5897     gp_XYZ aGC( 0.,0.,0. );
5898     TIDSortedElemSet newNodes;
5899
5900     itElem = theElements.begin();
5901     for ( ; itElem != theElements.end(); itElem++ ) {
5902       const SMDS_MeshElement* elem = *itElem;
5903
5904       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5905       while ( itN->more() ) {
5906         const SMDS_MeshElement* node = itN->next();
5907         if ( newNodes.insert( node ).second )
5908           aGC += SMESH_TNodeXYZ( node );
5909       }
5910     }
5911     aGC /= newNodes.size();
5912     aV0.SetXYZ( aGC );
5913   } // if (!theHasRefPoint) {
5914
5915   // 4. Processing the elements
5916   SMESHDS_Mesh* aMesh = GetMeshDS();
5917
5918   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5919     // check element type
5920     const SMDS_MeshElement* elem = *itElem;
5921     SMDSAbs_ElementType   aTypeE = elem->GetType();
5922     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5923       continue;
5924
5925     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5926     newNodesItVec.reserve( elem->NbNodes() );
5927
5928     // loop on elem nodes
5929     int nodeIndex = -1;
5930     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5931     while ( itN->more() )
5932     {
5933       ++nodeIndex;
5934       // check if a node has been already processed
5935       const SMDS_MeshNode* node =
5936         static_cast<const SMDS_MeshNode*>( itN->next() );
5937       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5938       if ( nIt == mapNewNodes.end() ) {
5939         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5940         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5941
5942         // make new nodes
5943         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5944         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5945         gp_Ax1 anAx1, anAxT1T0;
5946         gp_Dir aDT1x, aDT0x, aDT1T0;
5947
5948         aTolAng=1.e-4;
5949
5950         aV0x = aV0;
5951         aPN0 = SMESH_TNodeXYZ( node );
5952
5953         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5954         aP0x = aPP0.Pnt();
5955         aDT0x= aPP0.Tangent();
5956         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5957
5958         for ( int j = 1; j < aNbTP; ++j ) {
5959           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5960           aP1x     = aPP1.Pnt();
5961           aDT1x    = aPP1.Tangent();
5962           aAngle1x = aPP1.Angle();
5963
5964           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5965           // Translation
5966           gp_Vec aV01x( aP0x, aP1x );
5967           aTrsf.SetTranslation( aV01x );
5968
5969           // traslated point
5970           aV1x = aV0x.Transformed( aTrsf );
5971           aPN1 = aPN0.Transformed( aTrsf );
5972
5973           // rotation 1 [ T1,T0 ]
5974           aAngleT1T0=-aDT1x.Angle( aDT0x );
5975           if (fabs(aAngleT1T0) > aTolAng) {
5976             aDT1T0=aDT1x^aDT0x;
5977             anAxT1T0.SetLocation( aV1x );
5978             anAxT1T0.SetDirection( aDT1T0 );
5979             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5980
5981             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5982           }
5983
5984           // rotation 2
5985           if ( theHasAngles ) {
5986             anAx1.SetLocation( aV1x );
5987             anAx1.SetDirection( aDT1x );
5988             aTrsfRot.SetRotation( anAx1, aAngle1x );
5989
5990             aPN1 = aPN1.Transformed( aTrsfRot );
5991           }
5992
5993           // make new node
5994           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5995           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5996             // create additional node
5997             double x = ( aPN1.X() + aPN0.X() )/2.;
5998             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5999             double z = ( aPN1.Z() + aPN0.Z() )/2.;
6000             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6001             myLastCreatedNodes.Append(newNode);
6002             srcNodes.Append( node );
6003             listNewNodes.push_back( newNode );
6004           }
6005           const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6006           myLastCreatedNodes.Append(newNode);
6007           srcNodes.Append( node );
6008           listNewNodes.push_back( newNode );
6009
6010           aPN0 = aPN1;
6011           aP0x = aP1x;
6012           aV0x = aV1x;
6013           aDT0x = aDT1x;
6014         }
6015       }
6016
6017       else {
6018         // if current elem is quadratic and current node is not medium
6019         // we have to check - may be it is needed to insert additional nodes
6020         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6021           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6022           if(listNewNodes.size()==aNbTP-1) {
6023             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6024             gp_XYZ P(node->X(), node->Y(), node->Z());
6025             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6026             int i;
6027             for(i=0; i<aNbTP-1; i++) {
6028               const SMDS_MeshNode* N = *it;
6029               double x = ( N->X() + P.X() )/2.;
6030               double y = ( N->Y() + P.Y() )/2.;
6031               double z = ( N->Z() + P.Z() )/2.;
6032               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6033               srcNodes.Append( node );
6034               myLastCreatedNodes.Append(newN);
6035               aNodes[2*i] = newN;
6036               aNodes[2*i+1] = N;
6037               P = gp_XYZ(N->X(),N->Y(),N->Z());
6038             }
6039             listNewNodes.clear();
6040             for(i=0; i<2*(aNbTP-1); i++) {
6041               listNewNodes.push_back(aNodes[i]);
6042             }
6043           }
6044         }
6045       }
6046
6047       newNodesItVec.push_back( nIt );
6048     }
6049     // make new elements
6050     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6051     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6052     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6053   }
6054
6055   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6056
6057   if ( theMakeGroups )
6058     generateGroups( srcNodes, srcElems, "extruded");
6059
6060   return EXTR_OK;
6061 }
6062
6063
6064 //=======================================================================
6065 //function : LinearAngleVariation
6066 //purpose  : auxilary for ExtrusionAlongTrack
6067 //=======================================================================
6068 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6069                                             list<double>& Angles)
6070 {
6071   int nbAngles = Angles.size();
6072   if( nbSteps > nbAngles ) {
6073     vector<double> theAngles(nbAngles);
6074     list<double>::iterator it = Angles.begin();
6075     int i = -1;
6076     for(; it!=Angles.end(); it++) {
6077       i++;
6078       theAngles[i] = (*it);
6079     }
6080     list<double> res;
6081     double rAn2St = double( nbAngles ) / double( nbSteps );
6082     double angPrev = 0, angle;
6083     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6084       double angCur = rAn2St * ( iSt+1 );
6085       double angCurFloor  = floor( angCur );
6086       double angPrevFloor = floor( angPrev );
6087       if ( angPrevFloor == angCurFloor )
6088         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6089       else {
6090         int iP = int( angPrevFloor );
6091         double angPrevCeil = ceil(angPrev);
6092         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6093
6094         int iC = int( angCurFloor );
6095         if ( iC < nbAngles )
6096           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6097
6098         iP = int( angPrevCeil );
6099         while ( iC-- > iP )
6100           angle += theAngles[ iC ];
6101       }
6102       res.push_back(angle);
6103       angPrev = angCur;
6104     }
6105     Angles.clear();
6106     it = res.begin();
6107     for(; it!=res.end(); it++)
6108       Angles.push_back( *it );
6109   }
6110 }
6111
6112
6113 //================================================================================
6114 /*!
6115  * \brief Move or copy theElements applying theTrsf to their nodes
6116  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6117  *  \param theTrsf - transformation to apply
6118  *  \param theCopy - if true, create translated copies of theElems
6119  *  \param theMakeGroups - if true and theCopy, create translated groups
6120  *  \param theTargetMesh - mesh to copy translated elements into
6121  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6122  */
6123 //================================================================================
6124
6125 SMESH_MeshEditor::PGroupIDs
6126 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6127                              const gp_Trsf&     theTrsf,
6128                              const bool         theCopy,
6129                              const bool         theMakeGroups,
6130                              SMESH_Mesh*        theTargetMesh)
6131 {
6132   myLastCreatedElems.Clear();
6133   myLastCreatedNodes.Clear();
6134
6135   bool needReverse = false;
6136   string groupPostfix;
6137   switch ( theTrsf.Form() ) {
6138   case gp_PntMirror:
6139     MESSAGE("gp_PntMirror");
6140     needReverse = true;
6141     groupPostfix = "mirrored";
6142     break;
6143   case gp_Ax1Mirror:
6144     MESSAGE("gp_Ax1Mirror");
6145     groupPostfix = "mirrored";
6146     break;
6147   case gp_Ax2Mirror:
6148     MESSAGE("gp_Ax2Mirror");
6149     needReverse = true;
6150     groupPostfix = "mirrored";
6151     break;
6152   case gp_Rotation:
6153     MESSAGE("gp_Rotation");
6154     groupPostfix = "rotated";
6155     break;
6156   case gp_Translation:
6157     MESSAGE("gp_Translation");
6158     groupPostfix = "translated";
6159     break;
6160   case gp_Scale:
6161     MESSAGE("gp_Scale");
6162     groupPostfix = "scaled";
6163     break;
6164   case gp_CompoundTrsf: // different scale by axis
6165     MESSAGE("gp_CompoundTrsf");
6166     groupPostfix = "scaled";
6167     break;
6168   default:
6169     MESSAGE("default");
6170     needReverse = false;
6171     groupPostfix = "transformed";
6172   }
6173
6174   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6175   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6176   SMESHDS_Mesh* aMesh    = GetMeshDS();
6177
6178
6179   // map old node to new one
6180   TNodeNodeMap nodeMap;
6181
6182   // elements sharing moved nodes; those of them which have all
6183   // nodes mirrored but are not in theElems are to be reversed
6184   TIDSortedElemSet inverseElemSet;
6185
6186   // source elements for each generated one
6187   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6188
6189   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6190   TIDSortedElemSet orphanNode;
6191
6192   if ( theElems.empty() ) // transform the whole mesh
6193   {
6194     // add all elements
6195     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6196     while ( eIt->more() ) theElems.insert( eIt->next() );
6197     // add orphan nodes
6198     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6199     while ( nIt->more() )
6200     {
6201       const SMDS_MeshNode* node = nIt->next();
6202       if ( node->NbInverseElements() == 0)
6203         orphanNode.insert( node );
6204     }
6205   }
6206
6207   // loop on elements to transform nodes : first orphan nodes then elems
6208   TIDSortedElemSet::iterator itElem;
6209   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6210   for (int i=0; i<2; i++)
6211   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6212     const SMDS_MeshElement* elem = *itElem;
6213     if ( !elem )
6214       continue;
6215
6216     // loop on elem nodes
6217     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6218     while ( itN->more() ) {
6219
6220       const SMDS_MeshNode* node = cast2Node( itN->next() );
6221       // check if a node has been already transformed
6222       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6223         nodeMap.insert( make_pair ( node, node ));
6224       if ( !n2n_isnew.second )
6225         continue;
6226
6227       double coord[3];
6228       coord[0] = node->X();
6229       coord[1] = node->Y();
6230       coord[2] = node->Z();
6231       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6232       if ( theTargetMesh ) {
6233         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6234         n2n_isnew.first->second = newNode;
6235         myLastCreatedNodes.Append(newNode);
6236         srcNodes.Append( node );
6237       }
6238       else if ( theCopy ) {
6239         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6240         n2n_isnew.first->second = newNode;
6241         myLastCreatedNodes.Append(newNode);
6242         srcNodes.Append( node );
6243       }
6244       else {
6245         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6246         // node position on shape becomes invalid
6247         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6248           ( SMDS_SpacePosition::originSpacePosition() );
6249       }
6250
6251       // keep inverse elements
6252       if ( !theCopy && !theTargetMesh && needReverse ) {
6253         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6254         while ( invElemIt->more() ) {
6255           const SMDS_MeshElement* iel = invElemIt->next();
6256           inverseElemSet.insert( iel );
6257         }
6258       }
6259     }
6260   }
6261
6262   // either create new elements or reverse mirrored ones
6263   if ( !theCopy && !needReverse && !theTargetMesh )
6264     return PGroupIDs();
6265
6266   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6267   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6268     theElems.insert( *invElemIt );
6269
6270   // Replicate or reverse elements
6271
6272   std::vector<int> iForw;
6273   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6274   {
6275     const SMDS_MeshElement* elem = *itElem;
6276     if ( !elem ) continue;
6277
6278     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6279     int                  nbNodes  = elem->NbNodes();
6280     if ( geomType == SMDSGeom_NONE ) continue; // node
6281
6282     switch ( geomType ) {
6283
6284     case SMDSGeom_POLYGON:  // ---------------------- polygon
6285       {
6286         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6287         int iNode = 0;
6288         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6289         while (itN->more()) {
6290           const SMDS_MeshNode* node =
6291             static_cast<const SMDS_MeshNode*>(itN->next());
6292           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6293           if (nodeMapIt == nodeMap.end())
6294             break; // not all nodes transformed
6295           if (needReverse) {
6296             // reverse mirrored faces and volumes
6297             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6298           } else {
6299             poly_nodes[iNode] = (*nodeMapIt).second;
6300           }
6301           iNode++;
6302         }
6303         if ( iNode != nbNodes )
6304           continue; // not all nodes transformed
6305
6306         if ( theTargetMesh ) {
6307           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6308           srcElems.Append( elem );
6309         }
6310         else if ( theCopy ) {
6311           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6312           srcElems.Append( elem );
6313         }
6314         else {
6315           aMesh->ChangePolygonNodes(elem, poly_nodes);
6316         }
6317       }
6318       break;
6319
6320     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6321       {
6322         const SMDS_VtkVolume* aPolyedre =
6323           dynamic_cast<const SMDS_VtkVolume*>( elem );
6324         if (!aPolyedre) {
6325           MESSAGE("Warning: bad volumic element");
6326           continue;
6327         }
6328
6329         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6330         vector<int> quantities; quantities.reserve( nbNodes );
6331
6332         bool allTransformed = true;
6333         int nbFaces = aPolyedre->NbFaces();
6334         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6335           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6336           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6337             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6338             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6339             if (nodeMapIt == nodeMap.end()) {
6340               allTransformed = false; // not all nodes transformed
6341             } else {
6342               poly_nodes.push_back((*nodeMapIt).second);
6343             }
6344             if ( needReverse && allTransformed )
6345               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6346           }
6347           quantities.push_back(nbFaceNodes);
6348         }
6349         if ( !allTransformed )
6350           continue; // not all nodes transformed
6351
6352         if ( theTargetMesh ) {
6353           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6354           srcElems.Append( elem );
6355         }
6356         else if ( theCopy ) {
6357           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6358           srcElems.Append( elem );
6359         }
6360         else {
6361           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6362         }
6363       }
6364       break;
6365
6366     case SMDSGeom_BALL: // -------------------- Ball
6367       {
6368         if ( !theCopy && !theTargetMesh ) continue;
6369
6370         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6371         if (nodeMapIt == nodeMap.end())
6372           continue; // not all nodes transformed
6373
6374         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6375         if ( theTargetMesh ) {
6376           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6377           srcElems.Append( elem );
6378         }
6379         else {
6380           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6381           srcElems.Append( elem );
6382         }
6383       }
6384       break;
6385
6386     default: // ----------------------- Regular elements
6387
6388       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6389       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6390       const std::vector<int>& i = needReverse ? iRev : iForw;
6391
6392       // find transformed nodes
6393       vector<const SMDS_MeshNode*> nodes(nbNodes);
6394       int iNode = 0;
6395       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6396       while ( itN->more() ) {
6397         const SMDS_MeshNode* node =
6398           static_cast<const SMDS_MeshNode*>( itN->next() );
6399         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6400         if ( nodeMapIt == nodeMap.end() )
6401           break; // not all nodes transformed
6402         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6403       }
6404       if ( iNode != nbNodes )
6405         continue; // not all nodes transformed
6406
6407       if ( theTargetMesh ) {
6408         if ( SMDS_MeshElement* copy =
6409              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6410           myLastCreatedElems.Append( copy );
6411           srcElems.Append( elem );
6412         }
6413       }
6414       else if ( theCopy ) {
6415         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6416           srcElems.Append( elem );
6417       }
6418       else {
6419         // reverse element as it was reversed by transformation
6420         if ( nbNodes > 2 )
6421           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6422       }
6423     } // switch ( geomType )
6424
6425   } // loop on elements
6426
6427   PGroupIDs newGroupIDs;
6428
6429   if ( ( theMakeGroups && theCopy ) ||
6430        ( theMakeGroups && theTargetMesh ) )
6431     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6432
6433   return newGroupIDs;
6434 }
6435
6436 //=======================================================================
6437 /*!
6438  * \brief Create groups of elements made during transformation
6439  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6440  *  \param elemGens - elements making corresponding myLastCreatedElems
6441  *  \param postfix - to append to names of new groups
6442  *  \param targetMesh - mesh to create groups in
6443  *  \param topPresent - is there "top" elements that are created by sweeping
6444  */
6445 //=======================================================================
6446
6447 SMESH_MeshEditor::PGroupIDs
6448 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6449                                  const SMESH_SequenceOfElemPtr& elemGens,
6450                                  const std::string&             postfix,
6451                                  SMESH_Mesh*                    targetMesh,
6452                                  const bool                     topPresent)
6453 {
6454   PGroupIDs newGroupIDs( new list<int> );
6455   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6456
6457   // Sort existing groups by types and collect their names
6458
6459   // containers to store an old group and generated new ones;
6460   // 1st new group is for result elems of different type than a source one;
6461   // 2nd new group is for same type result elems ("top" group at extrusion)
6462   using boost::tuple;
6463   using boost::make_tuple;
6464   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6465   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6466   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6467   // group names
6468   set< string > groupNames;
6469
6470   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6471   if ( !groupIt->more() ) return newGroupIDs;
6472
6473   int newGroupID = mesh->GetGroupIds().back()+1;
6474   while ( groupIt->more() )
6475   {
6476     SMESH_Group * group = groupIt->next();
6477     if ( !group ) continue;
6478     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6479     if ( !groupDS || groupDS->IsEmpty() ) continue;
6480     groupNames.insert    ( group->GetName() );
6481     groupDS->SetStoreName( group->GetName() );
6482     const SMDSAbs_ElementType type = groupDS->GetType();
6483     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6484     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6485     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6486     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6487   }
6488
6489   // Loop on nodes and elements to add them in new groups
6490
6491   vector< const SMDS_MeshElement* > resultElems;
6492   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6493   {
6494     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6495     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6496     if ( gens.Length() != elems.Length() )
6497       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6498
6499     // loop on created elements
6500     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6501     {
6502       const SMDS_MeshElement* sourceElem = gens( iElem );
6503       if ( !sourceElem ) {
6504         MESSAGE("generateGroups(): NULL source element");
6505         continue;
6506       }
6507       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6508       if ( groupsOldNew.empty() ) { // no groups of this type at all
6509         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6510           ++iElem; // skip all elements made by sourceElem
6511         continue;
6512       }
6513       // collect all elements made by the iElem-th sourceElem
6514       resultElems.clear();
6515       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6516         if ( resElem != sourceElem )
6517           resultElems.push_back( resElem );
6518       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6519         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6520           if ( resElem != sourceElem )
6521             resultElems.push_back( resElem );
6522
6523       const SMDS_MeshElement* topElem = 0;
6524       if ( isNodes ) // there must be a top element
6525       {
6526         topElem = resultElems.back();
6527         resultElems.pop_back();
6528       }
6529       else
6530       {
6531         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6532         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6533           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6534           {
6535             topElem = *resElemIt;
6536             *resElemIt = 0; // erase *resElemIt
6537             break;
6538           }
6539       }
6540       // add resultElems to groups originted from ones the sourceElem belongs to
6541       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6542       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6543       {
6544         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6545         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6546         {
6547           // fill in a new group
6548           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6549           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6550           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6551             if ( *resElemIt )
6552               newGroup.Add( *resElemIt );
6553
6554           // fill a "top" group
6555           if ( topElem )
6556           {
6557             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6558             newTopGroup.Add( topElem );
6559          }
6560         }
6561       }
6562     } // loop on created elements
6563   }// loop on nodes and elements
6564
6565   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6566
6567   list<int> topGrouIds;
6568   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6569   {
6570     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6571     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6572                                       orderedOldNewGroups[i]->get<2>() };
6573     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6574     {
6575       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6576       if ( newGroupDS->IsEmpty() )
6577       {
6578         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6579       }
6580       else
6581       {
6582         // set group type
6583         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6584
6585         // make a name
6586         const bool isTop = ( topPresent &&
6587                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6588                              is2nd );
6589
6590         string name = oldGroupDS->GetStoreName();
6591         { // remove trailing whitespaces (issue 22599)
6592           size_t size = name.size();
6593           while ( size > 1 && isspace( name[ size-1 ]))
6594             --size;
6595           if ( size != name.size() )
6596           {
6597             name.resize( size );
6598             oldGroupDS->SetStoreName( name.c_str() );
6599           }
6600         }
6601         if ( !targetMesh ) {
6602           string suffix = ( isTop ? "top": postfix.c_str() );
6603           name += "_";
6604           name += suffix;
6605           int nb = 1;
6606           while ( !groupNames.insert( name ).second ) // name exists
6607             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6608         }
6609         else if ( isTop ) {
6610           name += "_top";
6611         }
6612         newGroupDS->SetStoreName( name.c_str() );
6613
6614         // make a SMESH_Groups
6615         mesh->AddGroup( newGroupDS );
6616         if ( isTop )
6617           topGrouIds.push_back( newGroupDS->GetID() );
6618         else
6619           newGroupIDs->push_back( newGroupDS->GetID() );
6620       }
6621     }
6622   }
6623   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6624
6625   return newGroupIDs;
6626 }
6627
6628 //================================================================================
6629 /*!
6630  * \brief Return list of group of nodes close to each other within theTolerance
6631  *        Search among theNodes or in the whole mesh if theNodes is empty using
6632  *        an Octree algorithm
6633  */
6634 //================================================================================
6635
6636 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6637                                             const double         theTolerance,
6638                                             TListOfListOfNodes & theGroupsOfNodes)
6639 {
6640   myLastCreatedElems.Clear();
6641   myLastCreatedNodes.Clear();
6642
6643   if ( theNodes.empty() )
6644   { // get all nodes in the mesh
6645     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6646     while ( nIt->more() )
6647       theNodes.insert( theNodes.end(),nIt->next());
6648   }
6649
6650   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6651 }
6652
6653 //=======================================================================
6654 //function : SimplifyFace
6655 //purpose  :
6656 //=======================================================================
6657
6658 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6659                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6660                                     vector<int>&                         quantities) const
6661 {
6662   int nbNodes = faceNodes.size();
6663
6664   if (nbNodes < 3)
6665     return 0;
6666
6667   set<const SMDS_MeshNode*> nodeSet;
6668
6669   // get simple seq of nodes
6670   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6671   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6672   int iSimple = 0, nbUnique = 0;
6673
6674   simpleNodes[iSimple++] = faceNodes[0];
6675   nbUnique++;
6676   for (int iCur = 1; iCur < nbNodes; iCur++) {
6677     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6678       simpleNodes[iSimple++] = faceNodes[iCur];
6679       if (nodeSet.insert( faceNodes[iCur] ).second)
6680         nbUnique++;
6681     }
6682   }
6683   int nbSimple = iSimple;
6684   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6685     nbSimple--;
6686     iSimple--;
6687   }
6688
6689   if (nbUnique < 3)
6690     return 0;
6691
6692   // separate loops
6693   int nbNew = 0;
6694   bool foundLoop = (nbSimple > nbUnique);
6695   while (foundLoop) {
6696     foundLoop = false;
6697     set<const SMDS_MeshNode*> loopSet;
6698     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6699       const SMDS_MeshNode* n = simpleNodes[iSimple];
6700       if (!loopSet.insert( n ).second) {
6701         foundLoop = true;
6702
6703         // separate loop
6704         int iC = 0, curLast = iSimple;
6705         for (; iC < curLast; iC++) {
6706           if (simpleNodes[iC] == n) break;
6707         }
6708         int loopLen = curLast - iC;
6709         if (loopLen > 2) {
6710           // create sub-element
6711           nbNew++;
6712           quantities.push_back(loopLen);
6713           for (; iC < curLast; iC++) {
6714             poly_nodes.push_back(simpleNodes[iC]);
6715           }
6716         }
6717         // shift the rest nodes (place from the first loop position)
6718         for (iC = curLast + 1; iC < nbSimple; iC++) {
6719           simpleNodes[iC - loopLen] = simpleNodes[iC];
6720         }
6721         nbSimple -= loopLen;
6722         iSimple -= loopLen;
6723       }
6724     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6725   } // while (foundLoop)
6726
6727   if (iSimple > 2) {
6728     nbNew++;
6729     quantities.push_back(iSimple);
6730     for (int i = 0; i < iSimple; i++)
6731       poly_nodes.push_back(simpleNodes[i]);
6732   }
6733
6734   return nbNew;
6735 }
6736
6737 //=======================================================================
6738 //function : MergeNodes
6739 //purpose  : In each group, the cdr of nodes are substituted by the first one
6740 //           in all elements.
6741 //=======================================================================
6742
6743 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6744 {
6745   MESSAGE("MergeNodes");
6746   myLastCreatedElems.Clear();
6747   myLastCreatedNodes.Clear();
6748
6749   SMESHDS_Mesh* aMesh = GetMeshDS();
6750
6751   TNodeNodeMap nodeNodeMap; // node to replace - new node
6752   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6753   list< int > rmElemIds, rmNodeIds;
6754
6755   // Fill nodeNodeMap and elems
6756
6757   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6758   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6759     list<const SMDS_MeshNode*>& nodes = *grIt;
6760     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6761     const SMDS_MeshNode* nToKeep = *nIt;
6762     //MESSAGE("node to keep " << nToKeep->GetID());
6763     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6764       const SMDS_MeshNode* nToRemove = *nIt;
6765       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6766       if ( nToRemove != nToKeep ) {
6767         //MESSAGE("  node to remove " << nToRemove->GetID());
6768         rmNodeIds.push_back( nToRemove->GetID() );
6769         AddToSameGroups( nToKeep, nToRemove, aMesh );
6770         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6771         // after MergeNodes() w/o creating node in place of merged ones.
6772         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6773         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6774           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6775             sm->SetIsAlwaysComputed( true );
6776       }
6777
6778       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6779       while ( invElemIt->more() ) {
6780         const SMDS_MeshElement* elem = invElemIt->next();
6781         elems.insert(elem);
6782       }
6783     }
6784   }
6785   // Change element nodes or remove an element
6786
6787   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6788   for ( ; eIt != elems.end(); eIt++ ) {
6789     const SMDS_MeshElement* elem = *eIt;
6790     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6791     int nbNodes = elem->NbNodes();
6792     int aShapeId = FindShape( elem );
6793
6794     set<const SMDS_MeshNode*> nodeSet;
6795     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6796     int iUnique = 0, iCur = 0, nbRepl = 0;
6797     vector<int> iRepl( nbNodes );
6798
6799     // get new seq of nodes
6800     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6801     while ( itN->more() ) {
6802       const SMDS_MeshNode* n =
6803         static_cast<const SMDS_MeshNode*>( itN->next() );
6804
6805       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6806       if ( nnIt != nodeNodeMap.end() ) { // n sticks
6807         n = (*nnIt).second;
6808         // BUG 0020185: begin
6809         {
6810           bool stopRecur = false;
6811           set<const SMDS_MeshNode*> nodesRecur;
6812           nodesRecur.insert(n);
6813           while (!stopRecur) {
6814             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6815             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6816               n = (*nnIt_i).second;
6817               if (!nodesRecur.insert(n).second) {
6818                 // error: recursive dependancy
6819                 stopRecur = true;
6820               }
6821             }
6822             else
6823               stopRecur = true;
6824           }
6825         }
6826         // BUG 0020185: end
6827       }
6828       curNodes[ iCur ] = n;
6829       bool isUnique = nodeSet.insert( n ).second;
6830       if ( isUnique )
6831         uniqueNodes[ iUnique++ ] = n;
6832       else
6833         iRepl[ nbRepl++ ] = iCur;
6834       iCur++;
6835     }
6836
6837     // Analyse element topology after replacement
6838
6839     bool isOk = true;
6840     int nbUniqueNodes = nodeSet.size();
6841     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6842     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6843       // Polygons and Polyhedral volumes
6844       if (elem->IsPoly()) {
6845
6846         if (elem->GetType() == SMDSAbs_Face) {
6847           // Polygon
6848           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6849           int inode = 0;
6850           for (; inode < nbNodes; inode++) {
6851             face_nodes[inode] = curNodes[inode];
6852           }
6853
6854           vector<const SMDS_MeshNode *> polygons_nodes;
6855           vector<int> quantities;
6856           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6857           if (nbNew > 0) {
6858             inode = 0;
6859             for (int iface = 0; iface < nbNew; iface++) {
6860               int nbNodes = quantities[iface];
6861               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6862               for (int ii = 0; ii < nbNodes; ii++, inode++) {
6863                 poly_nodes[ii] = polygons_nodes[inode];
6864               }
6865               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6866               myLastCreatedElems.Append(newElem);
6867               if (aShapeId)
6868                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6869             }
6870
6871             MESSAGE("ChangeElementNodes MergeNodes Polygon");
6872             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6873             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6874             int quid =0;
6875             if (nbNew > 0) quid = nbNew - 1;
6876             vector<int> newquant(quantities.begin()+quid, quantities.end());
6877             const SMDS_MeshElement* newElem = 0;
6878             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6879             myLastCreatedElems.Append(newElem);
6880             if ( aShapeId && newElem )
6881               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6882             rmElemIds.push_back(elem->GetID());
6883           }
6884           else {
6885             rmElemIds.push_back(elem->GetID());
6886           }
6887
6888         }
6889         else if (elem->GetType() == SMDSAbs_Volume) {
6890           // Polyhedral volume
6891           if (nbUniqueNodes < 4) {
6892             rmElemIds.push_back(elem->GetID());
6893           }
6894           else {
6895             // each face has to be analyzed in order to check volume validity
6896             const SMDS_VtkVolume* aPolyedre =
6897               dynamic_cast<const SMDS_VtkVolume*>( elem );
6898             if (aPolyedre) {
6899               int nbFaces = aPolyedre->NbFaces();
6900
6901               vector<const SMDS_MeshNode *> poly_nodes;
6902               vector<int> quantities;
6903
6904               for (int iface = 1; iface <= nbFaces; iface++) {
6905                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6906                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6907
6908                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6909                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6910                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6911                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6912                     faceNode = (*nnIt).second;
6913                   }
6914                   faceNodes[inode - 1] = faceNode;
6915                 }
6916
6917                 SimplifyFace(faceNodes, poly_nodes, quantities);
6918               }
6919
6920               if (quantities.size() > 3) {
6921                 // to be done: remove coincident faces
6922               }
6923
6924               if (quantities.size() > 3)
6925                 {
6926                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6927                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6928                   const SMDS_MeshElement* newElem = 0;
6929                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6930                   myLastCreatedElems.Append(newElem);
6931                   if ( aShapeId && newElem )
6932                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
6933                   rmElemIds.push_back(elem->GetID());
6934                 }
6935             }
6936             else {
6937               rmElemIds.push_back(elem->GetID());
6938             }
6939           }
6940         }
6941         else {
6942         }
6943
6944         continue;
6945       } // poly element
6946
6947       // Regular elements
6948       // TODO not all the possible cases are solved. Find something more generic?
6949       switch ( nbNodes ) {
6950       case 2: ///////////////////////////////////// EDGE
6951         isOk = false; break;
6952       case 3: ///////////////////////////////////// TRIANGLE
6953         isOk = false; break;
6954       case 4:
6955         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6956           isOk = false;
6957         else { //////////////////////////////////// QUADRANGLE
6958           if ( nbUniqueNodes < 3 )
6959             isOk = false;
6960           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6961             isOk = false; // opposite nodes stick
6962           //MESSAGE("isOk " << isOk);
6963         }
6964         break;
6965       case 6: ///////////////////////////////////// PENTAHEDRON
6966         if ( nbUniqueNodes == 4 ) {
6967           // ---------------------------------> tetrahedron
6968           if (nbRepl == 3 &&
6969               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6970             // all top nodes stick: reverse a bottom
6971             uniqueNodes[ 0 ] = curNodes [ 1 ];
6972             uniqueNodes[ 1 ] = curNodes [ 0 ];
6973           }
6974           else if (nbRepl == 3 &&
6975                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6976             // all bottom nodes stick: set a top before
6977             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6978             uniqueNodes[ 0 ] = curNodes [ 3 ];
6979             uniqueNodes[ 1 ] = curNodes [ 4 ];
6980             uniqueNodes[ 2 ] = curNodes [ 5 ];
6981           }
6982           else if (nbRepl == 4 &&
6983                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6984             // a lateral face turns into a line: reverse a bottom
6985             uniqueNodes[ 0 ] = curNodes [ 1 ];
6986             uniqueNodes[ 1 ] = curNodes [ 0 ];
6987           }
6988           else
6989             isOk = false;
6990         }
6991         else if ( nbUniqueNodes == 5 ) {
6992           // PENTAHEDRON --------------------> 2 tetrahedrons
6993           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6994             // a bottom node sticks with a linked top one
6995             // 1.
6996             SMDS_MeshElement* newElem =
6997               aMesh->AddVolume(curNodes[ 3 ],
6998                                curNodes[ 4 ],
6999                                curNodes[ 5 ],
7000                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7001             myLastCreatedElems.Append(newElem);
7002             if ( aShapeId )
7003               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7004             // 2. : reverse a bottom
7005             uniqueNodes[ 0 ] = curNodes [ 1 ];
7006             uniqueNodes[ 1 ] = curNodes [ 0 ];
7007             nbUniqueNodes = 4;
7008           }
7009           else
7010             isOk = false;
7011         }
7012         else
7013           isOk = false;
7014         break;
7015       case 8: {
7016         if(elem->IsQuadratic()) { // Quadratic quadrangle
7017           //   1    5    2
7018           //    +---+---+
7019           //    |       |
7020           //    |       |
7021           //   4+       +6
7022           //    |       |
7023           //    |       |
7024           //    +---+---+
7025           //   0    7    3
7026           isOk = false;
7027           if(nbRepl==2) {
7028             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7029           }
7030           if(nbRepl==3) {
7031             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7032             nbUniqueNodes = 6;
7033             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7034               uniqueNodes[0] = curNodes[0];
7035               uniqueNodes[1] = curNodes[2];
7036               uniqueNodes[2] = curNodes[3];
7037               uniqueNodes[3] = curNodes[5];
7038               uniqueNodes[4] = curNodes[6];
7039               uniqueNodes[5] = curNodes[7];
7040               isOk = true;
7041             }
7042             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7043               uniqueNodes[0] = curNodes[0];
7044               uniqueNodes[1] = curNodes[1];
7045               uniqueNodes[2] = curNodes[2];
7046               uniqueNodes[3] = curNodes[4];
7047               uniqueNodes[4] = curNodes[5];
7048               uniqueNodes[5] = curNodes[6];
7049               isOk = true;
7050             }
7051             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7052               uniqueNodes[0] = curNodes[1];
7053               uniqueNodes[1] = curNodes[2];
7054               uniqueNodes[2] = curNodes[3];
7055               uniqueNodes[3] = curNodes[5];
7056               uniqueNodes[4] = curNodes[6];
7057               uniqueNodes[5] = curNodes[0];
7058               isOk = true;
7059             }
7060             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7061               uniqueNodes[0] = curNodes[0];
7062               uniqueNodes[1] = curNodes[1];
7063               uniqueNodes[2] = curNodes[3];
7064               uniqueNodes[3] = curNodes[4];
7065               uniqueNodes[4] = curNodes[6];
7066               uniqueNodes[5] = curNodes[7];
7067               isOk = true;
7068             }
7069             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7070               uniqueNodes[0] = curNodes[0];
7071               uniqueNodes[1] = curNodes[2];
7072               uniqueNodes[2] = curNodes[3];
7073               uniqueNodes[3] = curNodes[1];
7074               uniqueNodes[4] = curNodes[6];
7075               uniqueNodes[5] = curNodes[7];
7076               isOk = true;
7077             }
7078             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7079               uniqueNodes[0] = curNodes[0];
7080               uniqueNodes[1] = curNodes[1];
7081               uniqueNodes[2] = curNodes[2];
7082               uniqueNodes[3] = curNodes[4];
7083               uniqueNodes[4] = curNodes[5];
7084               uniqueNodes[5] = curNodes[7];
7085               isOk = true;
7086             }
7087             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7088               uniqueNodes[0] = curNodes[0];
7089               uniqueNodes[1] = curNodes[1];
7090               uniqueNodes[2] = curNodes[3];
7091               uniqueNodes[3] = curNodes[4];
7092               uniqueNodes[4] = curNodes[2];
7093               uniqueNodes[5] = curNodes[7];
7094               isOk = true;
7095             }
7096             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7097               uniqueNodes[0] = curNodes[0];
7098               uniqueNodes[1] = curNodes[1];
7099               uniqueNodes[2] = curNodes[2];
7100               uniqueNodes[3] = curNodes[4];
7101               uniqueNodes[4] = curNodes[5];
7102               uniqueNodes[5] = curNodes[3];
7103               isOk = true;
7104             }
7105           }
7106           if(nbRepl==4) {
7107             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7108           }
7109           if(nbRepl==5) {
7110             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7111           }
7112           break;
7113         }
7114         //////////////////////////////////// HEXAHEDRON
7115         isOk = false;
7116         SMDS_VolumeTool hexa (elem);
7117         hexa.SetExternalNormal();
7118         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7119           //////////////////////// HEX ---> 1 tetrahedron
7120           for ( int iFace = 0; iFace < 6; iFace++ ) {
7121             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7122             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7123                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7124                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7125               // one face turns into a point ...
7126               int iOppFace = hexa.GetOppFaceIndex( iFace );
7127               ind = hexa.GetFaceNodesIndices( iOppFace );
7128               int nbStick = 0;
7129               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7130                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7131                   nbStick++;
7132               }
7133               if ( nbStick == 1 ) {
7134                 // ... and the opposite one - into a triangle.
7135                 // set a top node
7136                 ind = hexa.GetFaceNodesIndices( iFace );
7137                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7138                 isOk = true;
7139               }
7140               break;
7141             }
7142           }
7143         }
7144         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7145           //////////////////////// HEX ---> 1 prism
7146           int nbTria = 0, iTria[3];
7147           const int *ind; // indices of face nodes
7148           // look for triangular faces
7149           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7150             ind = hexa.GetFaceNodesIndices( iFace );
7151             TIDSortedNodeSet faceNodes;
7152             for ( iCur = 0; iCur < 4; iCur++ )
7153               faceNodes.insert( curNodes[ind[iCur]] );
7154             if ( faceNodes.size() == 3 )
7155               iTria[ nbTria++ ] = iFace;
7156           }
7157           // check if triangles are opposite
7158           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7159           {
7160             isOk = true;
7161             // set nodes of the bottom triangle
7162             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7163             vector<int> indB;
7164             for ( iCur = 0; iCur < 4; iCur++ )
7165               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7166                 indB.push_back( ind[iCur] );
7167             if ( !hexa.IsForward() )
7168               std::swap( indB[0], indB[2] );
7169             for ( iCur = 0; iCur < 3; iCur++ )
7170               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7171             // set nodes of the top triangle
7172             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7173             for ( iCur = 0; iCur < 3; ++iCur )
7174               for ( int j = 0; j < 4; ++j )
7175                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7176                 {
7177                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7178                   break;
7179                 }
7180           }
7181           break;
7182         }
7183         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7184           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7185           for ( int iFace = 0; iFace < 6; iFace++ ) {
7186             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7187             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7188                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7189                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7190               // one face turns into a point ...
7191               int iOppFace = hexa.GetOppFaceIndex( iFace );
7192               ind = hexa.GetFaceNodesIndices( iOppFace );
7193               int nbStick = 0;
7194               iUnique = 2;  // reverse a tetrahedron 1 bottom
7195               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7196                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7197                   nbStick++;
7198                 else if ( iUnique >= 0 )
7199                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7200               }
7201               if ( nbStick == 0 ) {
7202                 // ... and the opposite one is a quadrangle
7203                 // set a top node
7204                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7205                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7206                 nbUniqueNodes = 4;
7207                 // tetrahedron 2
7208                 SMDS_MeshElement* newElem =
7209                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7210                                    curNodes[ind[ 3 ]],
7211                                    curNodes[ind[ 2 ]],
7212                                    curNodes[indTop[ 0 ]]);
7213                 myLastCreatedElems.Append(newElem);
7214                 if ( aShapeId )
7215                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7216                 isOk = true;
7217               }
7218               break;
7219             }
7220           }
7221         }
7222         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7223           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7224           // find indices of quad and tri faces
7225           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7226           for ( iFace = 0; iFace < 6; iFace++ ) {
7227             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7228             nodeSet.clear();
7229             for ( iCur = 0; iCur < 4; iCur++ )
7230               nodeSet.insert( curNodes[ind[ iCur ]] );
7231             nbUniqueNodes = nodeSet.size();
7232             if ( nbUniqueNodes == 3 )
7233               iTriFace[ nbTri++ ] = iFace;
7234             else if ( nbUniqueNodes == 4 )
7235               iQuadFace[ nbQuad++ ] = iFace;
7236           }
7237           if (nbQuad == 2 && nbTri == 4 &&
7238               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7239             // 2 opposite quadrangles stuck with a diagonal;
7240             // sample groups of merged indices: (0-4)(2-6)
7241             // --------------------------------------------> 2 tetrahedrons
7242             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7243             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7244             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7245             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7246                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7247               // stuck with 0-2 diagonal
7248               i0  = ind1[ 3 ];
7249               i1d = ind1[ 0 ];
7250               i2  = ind1[ 1 ];
7251               i3d = ind1[ 2 ];
7252               i0t = ind2[ 1 ];
7253               i2t = ind2[ 3 ];
7254             }
7255             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7256                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7257               // stuck with 1-3 diagonal
7258               i0  = ind1[ 0 ];
7259               i1d = ind1[ 1 ];
7260               i2  = ind1[ 2 ];
7261               i3d = ind1[ 3 ];
7262               i0t = ind2[ 0 ];
7263               i2t = ind2[ 1 ];
7264             }
7265             else {
7266               ASSERT(0);
7267             }
7268             // tetrahedron 1
7269             uniqueNodes[ 0 ] = curNodes [ i0 ];
7270             uniqueNodes[ 1 ] = curNodes [ i1d ];
7271             uniqueNodes[ 2 ] = curNodes [ i3d ];
7272             uniqueNodes[ 3 ] = curNodes [ i0t ];
7273             nbUniqueNodes = 4;
7274             // tetrahedron 2
7275             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7276                                                          curNodes[ i2 ],
7277                                                          curNodes[ i3d ],
7278                                                          curNodes[ i2t ]);
7279             myLastCreatedElems.Append(newElem);
7280             if ( aShapeId )
7281               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7282             isOk = true;
7283           }
7284           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7285                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7286             // --------------------------------------------> prism
7287             // find 2 opposite triangles
7288             nbUniqueNodes = 6;
7289             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7290               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7291                 // find indices of kept and replaced nodes
7292                 // and fill unique nodes of 2 opposite triangles
7293                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7294                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7295                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7296                 // fill unique nodes
7297                 iUnique = 0;
7298                 isOk = true;
7299                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7300                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7301                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7302                   if ( n == nInit ) {
7303                     // iCur of a linked node of the opposite face (make normals co-directed):
7304                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7305                     // check that correspondent corners of triangles are linked
7306                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7307                       isOk = false;
7308                     else {
7309                       uniqueNodes[ iUnique ] = n;
7310                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7311                       iUnique++;
7312                     }
7313                   }
7314                 }
7315                 break;
7316               }
7317             }
7318           }
7319         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7320         else
7321         {
7322           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7323         }
7324         break;
7325       } // HEXAHEDRON
7326
7327       default:
7328         isOk = false;
7329       } // switch ( nbNodes )
7330
7331     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7332
7333     if ( isOk ) { // the elem remains valid after sticking nodes
7334       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7335       {
7336         // Change nodes of polyedre
7337         const SMDS_VtkVolume* aPolyedre =
7338           dynamic_cast<const SMDS_VtkVolume*>( elem );
7339         if (aPolyedre) {
7340           int nbFaces = aPolyedre->NbFaces();
7341
7342           vector<const SMDS_MeshNode *> poly_nodes;
7343           vector<int> quantities (nbFaces);
7344
7345           for (int iface = 1; iface <= nbFaces; iface++) {
7346             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7347             quantities[iface - 1] = nbFaceNodes;
7348
7349             for (inode = 1; inode <= nbFaceNodes; inode++) {
7350               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7351
7352               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7353               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7354                 curNode = (*nnIt).second;
7355               }
7356               poly_nodes.push_back(curNode);
7357             }
7358           }
7359           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7360         }
7361       }
7362       else // replace non-polyhedron elements
7363       {
7364         const SMDSAbs_ElementType etyp = elem->GetType();
7365         const int elemId               = elem->GetID();
7366         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7367         uniqueNodes.resize(nbUniqueNodes);
7368
7369         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7370
7371         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7372         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7373         if ( sm && newElem )
7374           sm->AddElement( newElem );
7375         if ( elem != newElem )
7376           ReplaceElemInGroups( elem, newElem, aMesh );
7377       }
7378     }
7379     else {
7380       // Remove invalid regular element or invalid polygon
7381       rmElemIds.push_back( elem->GetID() );
7382     }
7383
7384   } // loop on elements
7385
7386   // Remove bad elements, then equal nodes (order important)
7387
7388   Remove( rmElemIds, false );
7389   Remove( rmNodeIds, true );
7390
7391 }
7392
7393
7394 // ========================================================
7395 // class   : SortableElement
7396 // purpose : allow sorting elements basing on their nodes
7397 // ========================================================
7398 class SortableElement : public set <const SMDS_MeshElement*>
7399 {
7400 public:
7401
7402   SortableElement( const SMDS_MeshElement* theElem )
7403   {
7404     myElem = theElem;
7405     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7406     while ( nodeIt->more() )
7407       this->insert( nodeIt->next() );
7408   }
7409
7410   const SMDS_MeshElement* Get() const
7411   { return myElem; }
7412
7413   void Set(const SMDS_MeshElement* e) const
7414   { myElem = e; }
7415
7416
7417 private:
7418   mutable const SMDS_MeshElement* myElem;
7419 };
7420
7421 //=======================================================================
7422 //function : FindEqualElements
7423 //purpose  : Return list of group of elements built on the same nodes.
7424 //           Search among theElements or in the whole mesh if theElements is empty
7425 //=======================================================================
7426
7427 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7428                                          TListOfListOfElementsID & theGroupsOfElementsID)
7429 {
7430   myLastCreatedElems.Clear();
7431   myLastCreatedNodes.Clear();
7432
7433   typedef map< SortableElement, int > TMapOfNodeSet;
7434   typedef list<int> TGroupOfElems;
7435
7436   if ( theElements.empty() )
7437   { // get all elements in the mesh
7438     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7439     while ( eIt->more() )
7440       theElements.insert( theElements.end(), eIt->next());
7441   }
7442
7443   vector< TGroupOfElems > arrayOfGroups;
7444   TGroupOfElems groupOfElems;
7445   TMapOfNodeSet mapOfNodeSet;
7446
7447   TIDSortedElemSet::iterator elemIt = theElements.begin();
7448   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7449     const SMDS_MeshElement* curElem = *elemIt;
7450     SortableElement SE(curElem);
7451     int ind = -1;
7452     // check uniqueness
7453     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7454     if( !(pp.second) ) {
7455       TMapOfNodeSet::iterator& itSE = pp.first;
7456       ind = (*itSE).second;
7457       arrayOfGroups[ind].push_back(curElem->GetID());
7458     }
7459     else {
7460       groupOfElems.clear();
7461       groupOfElems.push_back(curElem->GetID());
7462       arrayOfGroups.push_back(groupOfElems);
7463       i++;
7464     }
7465   }
7466
7467   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7468   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7469     groupOfElems = *groupIt;
7470     if ( groupOfElems.size() > 1 ) {
7471       groupOfElems.sort();
7472       theGroupsOfElementsID.push_back(groupOfElems);
7473     }
7474   }
7475 }
7476
7477 //=======================================================================
7478 //function : MergeElements
7479 //purpose  : In each given group, substitute all elements by the first one.
7480 //=======================================================================
7481
7482 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7483 {
7484   myLastCreatedElems.Clear();
7485   myLastCreatedNodes.Clear();
7486
7487   typedef list<int> TListOfIDs;
7488   TListOfIDs rmElemIds; // IDs of elems to remove
7489
7490   SMESHDS_Mesh* aMesh = GetMeshDS();
7491
7492   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7493   while ( groupsIt != theGroupsOfElementsID.end() ) {
7494     TListOfIDs& aGroupOfElemID = *groupsIt;
7495     aGroupOfElemID.sort();
7496     int elemIDToKeep = aGroupOfElemID.front();
7497     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7498     aGroupOfElemID.pop_front();
7499     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7500     while ( idIt != aGroupOfElemID.end() ) {
7501       int elemIDToRemove = *idIt;
7502       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7503       // add the kept element in groups of removed one (PAL15188)
7504       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7505       rmElemIds.push_back( elemIDToRemove );
7506       ++idIt;
7507     }
7508     ++groupsIt;
7509   }
7510
7511   Remove( rmElemIds, false );
7512 }
7513
7514 //=======================================================================
7515 //function : MergeEqualElements
7516 //purpose  : Remove all but one of elements built on the same nodes.
7517 //=======================================================================
7518
7519 void SMESH_MeshEditor::MergeEqualElements()
7520 {
7521   TIDSortedElemSet aMeshElements; /* empty input ==
7522                                      to merge equal elements in the whole mesh */
7523   TListOfListOfElementsID aGroupsOfElementsID;
7524   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7525   MergeElements(aGroupsOfElementsID);
7526 }
7527
7528 //=======================================================================
7529 //function : findAdjacentFace
7530 //purpose  :
7531 //=======================================================================
7532
7533 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7534                                                 const SMDS_MeshNode* n2,
7535                                                 const SMDS_MeshElement* elem)
7536 {
7537   TIDSortedElemSet elemSet, avoidSet;
7538   if ( elem )
7539     avoidSet.insert ( elem );
7540   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7541 }
7542
7543 //=======================================================================
7544 //function : FindFreeBorder
7545 //purpose  :
7546 //=======================================================================
7547
7548 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7549
7550 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7551                                        const SMDS_MeshNode*             theSecondNode,
7552                                        const SMDS_MeshNode*             theLastNode,
7553                                        list< const SMDS_MeshNode* > &   theNodes,
7554                                        list< const SMDS_MeshElement* >& theFaces)
7555 {
7556   if ( !theFirstNode || !theSecondNode )
7557     return false;
7558   // find border face between theFirstNode and theSecondNode
7559   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7560   if ( !curElem )
7561     return false;
7562
7563   theFaces.push_back( curElem );
7564   theNodes.push_back( theFirstNode );
7565   theNodes.push_back( theSecondNode );
7566
7567   //vector<const SMDS_MeshNode*> nodes;
7568   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7569   TIDSortedElemSet foundElems;
7570   bool needTheLast = ( theLastNode != 0 );
7571
7572   while ( nStart != theLastNode ) {
7573     if ( nStart == theFirstNode )
7574       return !needTheLast;
7575
7576     // find all free border faces sharing form nStart
7577
7578     list< const SMDS_MeshElement* > curElemList;
7579     list< const SMDS_MeshNode* > nStartList;
7580     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7581     while ( invElemIt->more() ) {
7582       const SMDS_MeshElement* e = invElemIt->next();
7583       if ( e == curElem || foundElems.insert( e ).second ) {
7584         // get nodes
7585         int iNode = 0, nbNodes = e->NbNodes();
7586         //const SMDS_MeshNode* nodes[nbNodes+1];
7587         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7588
7589         if(e->IsQuadratic()) {
7590           const SMDS_VtkFace* F =
7591             dynamic_cast<const SMDS_VtkFace*>(e);
7592           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7593           // use special nodes iterator
7594           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7595           while( anIter->more() ) {
7596             nodes[ iNode++ ] = cast2Node(anIter->next());
7597           }
7598         }
7599         else {
7600           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7601           while ( nIt->more() )
7602             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7603         }
7604         nodes[ iNode ] = nodes[ 0 ];
7605         // check 2 links
7606         for ( iNode = 0; iNode < nbNodes; iNode++ )
7607           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7608                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7609               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7610           {
7611             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7612             curElemList.push_back( e );
7613           }
7614       }
7615     }
7616     // analyse the found
7617
7618     int nbNewBorders = curElemList.size();
7619     if ( nbNewBorders == 0 ) {
7620       // no free border furthermore
7621       return !needTheLast;
7622     }
7623     else if ( nbNewBorders == 1 ) {
7624       // one more element found
7625       nIgnore = nStart;
7626       nStart = nStartList.front();
7627       curElem = curElemList.front();
7628       theFaces.push_back( curElem );
7629       theNodes.push_back( nStart );
7630     }
7631     else {
7632       // several continuations found
7633       list< const SMDS_MeshElement* >::iterator curElemIt;
7634       list< const SMDS_MeshNode* >::iterator nStartIt;
7635       // check if one of them reached the last node
7636       if ( needTheLast ) {
7637         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7638              curElemIt!= curElemList.end();
7639              curElemIt++, nStartIt++ )
7640           if ( *nStartIt == theLastNode ) {
7641             theFaces.push_back( *curElemIt );
7642             theNodes.push_back( *nStartIt );
7643             return true;
7644           }
7645       }
7646       // find the best free border by the continuations
7647       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7648       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7649       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7650            curElemIt!= curElemList.end();
7651            curElemIt++, nStartIt++ )
7652       {
7653         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7654         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7655         // find one more free border
7656         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7657           cNL->clear();
7658           cFL->clear();
7659         }
7660         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7661           // choice: clear a worse one
7662           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7663           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7664           contNodes[ iWorse ].clear();
7665           contFaces[ iWorse ].clear();
7666         }
7667       }
7668       if ( contNodes[0].empty() && contNodes[1].empty() )
7669         return false;
7670
7671       // append the best free border
7672       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7673       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7674       theNodes.pop_back(); // remove nIgnore
7675       theNodes.pop_back(); // remove nStart
7676       theFaces.pop_back(); // remove curElem
7677       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7678       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7679       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7680       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7681       return true;
7682
7683     } // several continuations found
7684   } // while ( nStart != theLastNode )
7685
7686   return true;
7687 }
7688
7689 //=======================================================================
7690 //function : CheckFreeBorderNodes
7691 //purpose  : Return true if the tree nodes are on a free border
7692 //=======================================================================
7693
7694 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7695                                             const SMDS_MeshNode* theNode2,
7696                                             const SMDS_MeshNode* theNode3)
7697 {
7698   list< const SMDS_MeshNode* > nodes;
7699   list< const SMDS_MeshElement* > faces;
7700   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7701 }
7702
7703 //=======================================================================
7704 //function : SewFreeBorder
7705 //purpose  :
7706 //=======================================================================
7707
7708 SMESH_MeshEditor::Sew_Error
7709 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7710                                  const SMDS_MeshNode* theBordSecondNode,
7711                                  const SMDS_MeshNode* theBordLastNode,
7712                                  const SMDS_MeshNode* theSideFirstNode,
7713                                  const SMDS_MeshNode* theSideSecondNode,
7714                                  const SMDS_MeshNode* theSideThirdNode,
7715                                  const bool           theSideIsFreeBorder,
7716                                  const bool           toCreatePolygons,
7717                                  const bool           toCreatePolyedrs)
7718 {
7719   myLastCreatedElems.Clear();
7720   myLastCreatedNodes.Clear();
7721
7722   MESSAGE("::SewFreeBorder()");
7723   Sew_Error aResult = SEW_OK;
7724
7725   // ====================================
7726   //    find side nodes and elements
7727   // ====================================
7728
7729   list< const SMDS_MeshNode* > nSide[ 2 ];
7730   list< const SMDS_MeshElement* > eSide[ 2 ];
7731   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7732   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7733
7734   // Free border 1
7735   // --------------
7736   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7737                       nSide[0], eSide[0])) {
7738     MESSAGE(" Free Border 1 not found " );
7739     aResult = SEW_BORDER1_NOT_FOUND;
7740   }
7741   if (theSideIsFreeBorder) {
7742     // Free border 2
7743     // --------------
7744     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7745                         nSide[1], eSide[1])) {
7746       MESSAGE(" Free Border 2 not found " );
7747       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7748     }
7749   }
7750   if ( aResult != SEW_OK )
7751     return aResult;
7752
7753   if (!theSideIsFreeBorder) {
7754     // Side 2
7755     // --------------
7756
7757     // -------------------------------------------------------------------------
7758     // Algo:
7759     // 1. If nodes to merge are not coincident, move nodes of the free border
7760     //    from the coord sys defined by the direction from the first to last
7761     //    nodes of the border to the correspondent sys of the side 2
7762     // 2. On the side 2, find the links most co-directed with the correspondent
7763     //    links of the free border
7764     // -------------------------------------------------------------------------
7765
7766     // 1. Since sewing may break if there are volumes to split on the side 2,
7767     //    we wont move nodes but just compute new coordinates for them
7768     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7769     TNodeXYZMap nBordXYZ;
7770     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7771     list< const SMDS_MeshNode* >::iterator nBordIt;
7772
7773     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7774     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7775     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7776     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7777     double tol2 = 1.e-8;
7778     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7779     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7780       // Need node movement.
7781
7782       // find X and Z axes to create trsf
7783       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7784       gp_Vec X = Zs ^ Zb;
7785       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7786         // Zb || Zs
7787         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7788
7789       // coord systems
7790       gp_Ax3 toBordAx( Pb1, Zb, X );
7791       gp_Ax3 fromSideAx( Ps1, Zs, X );
7792       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7793       // set trsf
7794       gp_Trsf toBordSys, fromSide2Sys;
7795       toBordSys.SetTransformation( toBordAx );
7796       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7797       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7798
7799       // move
7800       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7801         const SMDS_MeshNode* n = *nBordIt;
7802         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7803         toBordSys.Transforms( xyz );
7804         fromSide2Sys.Transforms( xyz );
7805         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7806       }
7807     }
7808     else {
7809       // just insert nodes XYZ in the nBordXYZ map
7810       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7811         const SMDS_MeshNode* n = *nBordIt;
7812         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7813       }
7814     }
7815
7816     // 2. On the side 2, find the links most co-directed with the correspondent
7817     //    links of the free border
7818
7819     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7820     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7821     sideNodes.push_back( theSideFirstNode );
7822
7823     bool hasVolumes = false;
7824     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7825     set<long> foundSideLinkIDs, checkedLinkIDs;
7826     SMDS_VolumeTool volume;
7827     //const SMDS_MeshNode* faceNodes[ 4 ];
7828
7829     const SMDS_MeshNode*    sideNode;
7830     const SMDS_MeshElement* sideElem;
7831     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7832     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7833     nBordIt = bordNodes.begin();
7834     nBordIt++;
7835     // border node position and border link direction to compare with
7836     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7837     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7838     // choose next side node by link direction or by closeness to
7839     // the current border node:
7840     bool searchByDir = ( *nBordIt != theBordLastNode );
7841     do {
7842       // find the next node on the Side 2
7843       sideNode = 0;
7844       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7845       long linkID;
7846       checkedLinkIDs.clear();
7847       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7848
7849       // loop on inverse elements of current node (prevSideNode) on the Side 2
7850       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7851       while ( invElemIt->more() )
7852       {
7853         const SMDS_MeshElement* elem = invElemIt->next();
7854         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7855         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7856         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7857         bool isVolume = volume.Set( elem );
7858         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7859         if ( isVolume ) // --volume
7860           hasVolumes = true;
7861         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7862           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7863           if(elem->IsQuadratic()) {
7864             const SMDS_VtkFace* F =
7865               dynamic_cast<const SMDS_VtkFace*>(elem);
7866             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7867             // use special nodes iterator
7868             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7869             while( anIter->more() ) {
7870               nodes[ iNode ] = cast2Node(anIter->next());
7871               if ( nodes[ iNode++ ] == prevSideNode )
7872                 iPrevNode = iNode - 1;
7873             }
7874           }
7875           else {
7876             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7877             while ( nIt->more() ) {
7878               nodes[ iNode ] = cast2Node( nIt->next() );
7879               if ( nodes[ iNode++ ] == prevSideNode )
7880                 iPrevNode = iNode - 1;
7881             }
7882           }
7883           // there are 2 links to check
7884           nbNodes = 2;
7885         }
7886         else // --edge
7887           continue;
7888         // loop on links, to be precise, on the second node of links
7889         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7890           const SMDS_MeshNode* n = nodes[ iNode ];
7891           if ( isVolume ) {
7892             if ( !volume.IsLinked( n, prevSideNode ))
7893               continue;
7894           }
7895           else {
7896             if ( iNode ) // a node before prevSideNode
7897               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7898             else         // a node after prevSideNode
7899               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7900           }
7901           // check if this link was already used
7902           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7903           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7904           if (!isJustChecked &&
7905               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7906           {
7907             // test a link geometrically
7908             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7909             bool linkIsBetter = false;
7910             double dot = 0.0, dist = 0.0;
7911             if ( searchByDir ) { // choose most co-directed link
7912               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7913               linkIsBetter = ( dot > maxDot );
7914             }
7915             else { // choose link with the node closest to bordPos
7916               dist = ( nextXYZ - bordPos ).SquareModulus();
7917               linkIsBetter = ( dist < minDist );
7918             }
7919             if ( linkIsBetter ) {
7920               maxDot = dot;
7921               minDist = dist;
7922               linkID = iLink;
7923               sideNode = n;
7924               sideElem = elem;
7925             }
7926           }
7927         }
7928       } // loop on inverse elements of prevSideNode
7929
7930       if ( !sideNode ) {
7931         MESSAGE(" Cant find path by links of the Side 2 ");
7932         return SEW_BAD_SIDE_NODES;
7933       }
7934       sideNodes.push_back( sideNode );
7935       sideElems.push_back( sideElem );
7936       foundSideLinkIDs.insert ( linkID );
7937       prevSideNode = sideNode;
7938
7939       if ( *nBordIt == theBordLastNode )
7940         searchByDir = false;
7941       else {
7942         // find the next border link to compare with
7943         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7944         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7945         // move to next border node if sideNode is before forward border node (bordPos)
7946         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7947           prevBordNode = *nBordIt;
7948           nBordIt++;
7949           bordPos = nBordXYZ[ *nBordIt ];
7950           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7951           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7952         }
7953       }
7954     }
7955     while ( sideNode != theSideSecondNode );
7956
7957     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7958       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7959       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7960     }
7961   } // end nodes search on the side 2
7962
7963   // ============================
7964   // sew the border to the side 2
7965   // ============================
7966
7967   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
7968   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7969
7970   TListOfListOfNodes nodeGroupsToMerge;
7971   if ( nbNodes[0] == nbNodes[1] ||
7972        ( theSideIsFreeBorder && !theSideThirdNode)) {
7973
7974     // all nodes are to be merged
7975
7976     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7977          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7978          nIt[0]++, nIt[1]++ )
7979     {
7980       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7981       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7982       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7983     }
7984   }
7985   else {
7986
7987     // insert new nodes into the border and the side to get equal nb of segments
7988
7989     // get normalized parameters of nodes on the borders
7990     //double param[ 2 ][ maxNbNodes ];
7991     double* param[ 2 ];
7992     param[0] = new double [ maxNbNodes ];
7993     param[1] = new double [ maxNbNodes ];
7994     int iNode, iBord;
7995     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7996       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7997       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7998       const SMDS_MeshNode* nPrev = *nIt;
7999       double bordLength = 0;
8000       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8001         const SMDS_MeshNode* nCur = *nIt;
8002         gp_XYZ segment (nCur->X() - nPrev->X(),
8003                         nCur->Y() - nPrev->Y(),
8004                         nCur->Z() - nPrev->Z());
8005         double segmentLen = segment.Modulus();
8006         bordLength += segmentLen;
8007         param[ iBord ][ iNode ] = bordLength;
8008         nPrev = nCur;
8009       }
8010       // normalize within [0,1]
8011       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8012         param[ iBord ][ iNode ] /= bordLength;
8013       }
8014     }
8015
8016     // loop on border segments
8017     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8018     int i[ 2 ] = { 0, 0 };
8019     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8020     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8021
8022     TElemOfNodeListMap insertMap;
8023     TElemOfNodeListMap::iterator insertMapIt;
8024     // insertMap is
8025     // key:   elem to insert nodes into
8026     // value: 2 nodes to insert between + nodes to be inserted
8027     do {
8028       bool next[ 2 ] = { false, false };
8029
8030       // find min adjacent segment length after sewing
8031       double nextParam = 10., prevParam = 0;
8032       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8033         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8034           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8035         if ( i[ iBord ] > 0 )
8036           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8037       }
8038       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8039       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8040       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8041
8042       // choose to insert or to merge nodes
8043       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8044       if ( Abs( du ) <= minSegLen * 0.2 ) {
8045         // merge
8046         // ------
8047         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8048         const SMDS_MeshNode* n0 = *nIt[0];
8049         const SMDS_MeshNode* n1 = *nIt[1];
8050         nodeGroupsToMerge.back().push_back( n1 );
8051         nodeGroupsToMerge.back().push_back( n0 );
8052         // position of node of the border changes due to merge
8053         param[ 0 ][ i[0] ] += du;
8054         // move n1 for the sake of elem shape evaluation during insertion.
8055         // n1 will be removed by MergeNodes() anyway
8056         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8057         next[0] = next[1] = true;
8058       }
8059       else {
8060         // insert
8061         // ------
8062         int intoBord = ( du < 0 ) ? 0 : 1;
8063         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8064         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8065         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8066         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8067         if ( intoBord == 1 ) {
8068           // move node of the border to be on a link of elem of the side
8069           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8070           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8071           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8072           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8073           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8074         }
8075         insertMapIt = insertMap.find( elem );
8076         bool notFound = ( insertMapIt == insertMap.end() );
8077         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8078         if ( otherLink ) {
8079           // insert into another link of the same element:
8080           // 1. perform insertion into the other link of the elem
8081           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8082           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8083           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8084           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8085           // 2. perform insertion into the link of adjacent faces
8086           while (true) {
8087             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8088             if ( adjElem )
8089               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8090             else
8091               break;
8092           }
8093           if (toCreatePolyedrs) {
8094             // perform insertion into the links of adjacent volumes
8095             UpdateVolumes(n12, n22, nodeList);
8096           }
8097           // 3. find an element appeared on n1 and n2 after the insertion
8098           insertMap.erase( elem );
8099           elem = findAdjacentFace( n1, n2, 0 );
8100         }
8101         if ( notFound || otherLink ) {
8102           // add element and nodes of the side into the insertMap
8103           insertMapIt = insertMap.insert
8104             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8105           (*insertMapIt).second.push_back( n1 );
8106           (*insertMapIt).second.push_back( n2 );
8107         }
8108         // add node to be inserted into elem
8109         (*insertMapIt).second.push_back( nIns );
8110         next[ 1 - intoBord ] = true;
8111       }
8112
8113       // go to the next segment
8114       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8115         if ( next[ iBord ] ) {
8116           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8117             eIt[ iBord ]++;
8118           nPrev[ iBord ] = *nIt[ iBord ];
8119           nIt[ iBord ]++; i[ iBord ]++;
8120         }
8121       }
8122     }
8123     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8124
8125     // perform insertion of nodes into elements
8126
8127     for (insertMapIt = insertMap.begin();
8128          insertMapIt != insertMap.end();
8129          insertMapIt++ )
8130     {
8131       const SMDS_MeshElement* elem = (*insertMapIt).first;
8132       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8133       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8134       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8135
8136       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8137
8138       if ( !theSideIsFreeBorder ) {
8139         // look for and insert nodes into the faces adjacent to elem
8140         while (true) {
8141           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8142           if ( adjElem )
8143             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8144           else
8145             break;
8146         }
8147       }
8148       if (toCreatePolyedrs) {
8149         // perform insertion into the links of adjacent volumes
8150         UpdateVolumes(n1, n2, nodeList);
8151       }
8152     }
8153
8154     delete param[0];
8155     delete param[1];
8156   } // end: insert new nodes
8157
8158   MergeNodes ( nodeGroupsToMerge );
8159
8160   return aResult;
8161 }
8162
8163 //=======================================================================
8164 //function : InsertNodesIntoLink
8165 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8166 //           and theBetweenNode2 and split theElement
8167 //=======================================================================
8168
8169 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8170                                            const SMDS_MeshNode*        theBetweenNode1,
8171                                            const SMDS_MeshNode*        theBetweenNode2,
8172                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8173                                            const bool                  toCreatePoly)
8174 {
8175   if ( theFace->GetType() != SMDSAbs_Face ) return;
8176
8177   // find indices of 2 link nodes and of the rest nodes
8178   int iNode = 0, il1, il2, i3, i4;
8179   il1 = il2 = i3 = i4 = -1;
8180   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8181   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8182
8183   if(theFace->IsQuadratic()) {
8184     const SMDS_VtkFace* F =
8185       dynamic_cast<const SMDS_VtkFace*>(theFace);
8186     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8187     // use special nodes iterator
8188     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8189     while( anIter->more() ) {
8190       const SMDS_MeshNode* n = cast2Node(anIter->next());
8191       if ( n == theBetweenNode1 )
8192         il1 = iNode;
8193       else if ( n == theBetweenNode2 )
8194         il2 = iNode;
8195       else if ( i3 < 0 )
8196         i3 = iNode;
8197       else
8198         i4 = iNode;
8199       nodes[ iNode++ ] = n;
8200     }
8201   }
8202   else {
8203     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8204     while ( nodeIt->more() ) {
8205       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8206       if ( n == theBetweenNode1 )
8207         il1 = iNode;
8208       else if ( n == theBetweenNode2 )
8209         il2 = iNode;
8210       else if ( i3 < 0 )
8211         i3 = iNode;
8212       else
8213         i4 = iNode;
8214       nodes[ iNode++ ] = n;
8215     }
8216   }
8217   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8218     return ;
8219
8220   // arrange link nodes to go one after another regarding the face orientation
8221   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8222   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8223   if ( reverse ) {
8224     iNode = il1;
8225     il1 = il2;
8226     il2 = iNode;
8227     aNodesToInsert.reverse();
8228   }
8229   // check that not link nodes of a quadrangles are in good order
8230   int nbFaceNodes = theFace->NbNodes();
8231   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8232     iNode = i3;
8233     i3 = i4;
8234     i4 = iNode;
8235   }
8236
8237   if (toCreatePoly || theFace->IsPoly()) {
8238
8239     iNode = 0;
8240     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8241
8242     // add nodes of face up to first node of link
8243     bool isFLN = false;
8244
8245     if(theFace->IsQuadratic()) {
8246       const SMDS_VtkFace* F =
8247         dynamic_cast<const SMDS_VtkFace*>(theFace);
8248       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8249       // use special nodes iterator
8250       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8251       while( anIter->more()  && !isFLN ) {
8252         const SMDS_MeshNode* n = cast2Node(anIter->next());
8253         poly_nodes[iNode++] = n;
8254         if (n == nodes[il1]) {
8255           isFLN = true;
8256         }
8257       }
8258       // add nodes to insert
8259       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8260       for (; nIt != aNodesToInsert.end(); nIt++) {
8261         poly_nodes[iNode++] = *nIt;
8262       }
8263       // add nodes of face starting from last node of link
8264       while ( anIter->more() ) {
8265         poly_nodes[iNode++] = cast2Node(anIter->next());
8266       }
8267     }
8268     else {
8269       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8270       while ( nodeIt->more() && !isFLN ) {
8271         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8272         poly_nodes[iNode++] = n;
8273         if (n == nodes[il1]) {
8274           isFLN = true;
8275         }
8276       }
8277       // add nodes to insert
8278       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8279       for (; nIt != aNodesToInsert.end(); nIt++) {
8280         poly_nodes[iNode++] = *nIt;
8281       }
8282       // add nodes of face starting from last node of link
8283       while ( nodeIt->more() ) {
8284         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8285         poly_nodes[iNode++] = n;
8286       }
8287     }
8288
8289     // edit or replace the face
8290     SMESHDS_Mesh *aMesh = GetMeshDS();
8291
8292     if (theFace->IsPoly()) {
8293       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8294     }
8295     else {
8296       int aShapeId = FindShape( theFace );
8297
8298       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8299       myLastCreatedElems.Append(newElem);
8300       if ( aShapeId && newElem )
8301         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8302
8303       aMesh->RemoveElement(theFace);
8304     }
8305     return;
8306   }
8307
8308   SMESHDS_Mesh *aMesh = GetMeshDS();
8309   if( !theFace->IsQuadratic() ) {
8310
8311     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8312     int nbLinkNodes = 2 + aNodesToInsert.size();
8313     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8314     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8315     linkNodes[ 0 ] = nodes[ il1 ];
8316     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8317     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8318     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8319       linkNodes[ iNode++ ] = *nIt;
8320     }
8321     // decide how to split a quadrangle: compare possible variants
8322     // and choose which of splits to be a quadrangle
8323     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8324     if ( nbFaceNodes == 3 ) {
8325       iBestQuad = nbSplits;
8326       i4 = i3;
8327     }
8328     else if ( nbFaceNodes == 4 ) {
8329       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8330       double aBestRate = DBL_MAX;
8331       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8332         i1 = 0; i2 = 1;
8333         double aBadRate = 0;
8334         // evaluate elements quality
8335         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8336           if ( iSplit == iQuad ) {
8337             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8338                                    linkNodes[ i2++ ],
8339                                    nodes[ i3 ],
8340                                    nodes[ i4 ]);
8341             aBadRate += getBadRate( &quad, aCrit );
8342           }
8343           else {
8344             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8345                                    linkNodes[ i2++ ],
8346                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8347             aBadRate += getBadRate( &tria, aCrit );
8348           }
8349         }
8350         // choice
8351         if ( aBadRate < aBestRate ) {
8352           iBestQuad = iQuad;
8353           aBestRate = aBadRate;
8354         }
8355       }
8356     }
8357
8358     // create new elements
8359     int aShapeId = FindShape( theFace );
8360
8361     i1 = 0; i2 = 1;
8362     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8363       SMDS_MeshElement* newElem = 0;
8364       if ( iSplit == iBestQuad )
8365         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8366                                   linkNodes[ i2++ ],
8367                                   nodes[ i3 ],
8368                                   nodes[ i4 ]);
8369       else
8370         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8371                                   linkNodes[ i2++ ],
8372                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8373       myLastCreatedElems.Append(newElem);
8374       if ( aShapeId && newElem )
8375         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8376     }
8377
8378     // change nodes of theFace
8379     const SMDS_MeshNode* newNodes[ 4 ];
8380     newNodes[ 0 ] = linkNodes[ i1 ];
8381     newNodes[ 1 ] = linkNodes[ i2 ];
8382     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8383     newNodes[ 3 ] = nodes[ i4 ];
8384     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8385     const SMDS_MeshElement* newElem = 0;
8386     if (iSplit == iBestQuad)
8387       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8388     else
8389       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8390     myLastCreatedElems.Append(newElem);
8391     if ( aShapeId && newElem )
8392       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8393 } // end if(!theFace->IsQuadratic())
8394   else { // theFace is quadratic
8395     // we have to split theFace on simple triangles and one simple quadrangle
8396     int tmp = il1/2;
8397     int nbshift = tmp*2;
8398     // shift nodes in nodes[] by nbshift
8399     int i,j;
8400     for(i=0; i<nbshift; i++) {
8401       const SMDS_MeshNode* n = nodes[0];
8402       for(j=0; j<nbFaceNodes-1; j++) {
8403         nodes[j] = nodes[j+1];
8404       }
8405       nodes[nbFaceNodes-1] = n;
8406     }
8407     il1 = il1 - nbshift;
8408     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8409     //   n0      n1     n2    n0      n1     n2
8410     //     +-----+-----+        +-----+-----+
8411     //      \         /         |           |
8412     //       \       /          |           |
8413     //      n5+     +n3       n7+           +n3
8414     //         \   /            |           |
8415     //          \ /             |           |
8416     //           +              +-----+-----+
8417     //           n4           n6      n5     n4
8418
8419     // create new elements
8420     int aShapeId = FindShape( theFace );
8421
8422     int n1,n2,n3;
8423     if(nbFaceNodes==6) { // quadratic triangle
8424       SMDS_MeshElement* newElem =
8425         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8426       myLastCreatedElems.Append(newElem);
8427       if ( aShapeId && newElem )
8428         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8429       if(theFace->IsMediumNode(nodes[il1])) {
8430         // create quadrangle
8431         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8432         myLastCreatedElems.Append(newElem);
8433         if ( aShapeId && newElem )
8434           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8435         n1 = 1;
8436         n2 = 2;
8437         n3 = 3;
8438       }
8439       else {
8440         // create quadrangle
8441         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8442         myLastCreatedElems.Append(newElem);
8443         if ( aShapeId && newElem )
8444           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8445         n1 = 0;
8446         n2 = 1;
8447         n3 = 5;
8448       }
8449     }
8450     else { // nbFaceNodes==8 - quadratic quadrangle
8451       SMDS_MeshElement* newElem =
8452         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8453       myLastCreatedElems.Append(newElem);
8454       if ( aShapeId && newElem )
8455         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8456       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8457       myLastCreatedElems.Append(newElem);
8458       if ( aShapeId && newElem )
8459         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8460       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8461       myLastCreatedElems.Append(newElem);
8462       if ( aShapeId && newElem )
8463         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8464       if(theFace->IsMediumNode(nodes[il1])) {
8465         // create quadrangle
8466         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8467         myLastCreatedElems.Append(newElem);
8468         if ( aShapeId && newElem )
8469           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8470         n1 = 1;
8471         n2 = 2;
8472         n3 = 3;
8473       }
8474       else {
8475         // create quadrangle
8476         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8477         myLastCreatedElems.Append(newElem);
8478         if ( aShapeId && newElem )
8479           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8480         n1 = 0;
8481         n2 = 1;
8482         n3 = 7;
8483       }
8484     }
8485     // create needed triangles using n1,n2,n3 and inserted nodes
8486     int nbn = 2 + aNodesToInsert.size();
8487     //const SMDS_MeshNode* aNodes[nbn];
8488     vector<const SMDS_MeshNode*> aNodes(nbn);
8489     aNodes[0] = nodes[n1];
8490     aNodes[nbn-1] = nodes[n2];
8491     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8492     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8493       aNodes[iNode++] = *nIt;
8494     }
8495     for(i=1; i<nbn; i++) {
8496       SMDS_MeshElement* newElem =
8497         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8498       myLastCreatedElems.Append(newElem);
8499       if ( aShapeId && newElem )
8500         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8501     }
8502   }
8503   // remove old face
8504   aMesh->RemoveElement(theFace);
8505 }
8506
8507 //=======================================================================
8508 //function : UpdateVolumes
8509 //purpose  :
8510 //=======================================================================
8511 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8512                                       const SMDS_MeshNode*        theBetweenNode2,
8513                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8514 {
8515   myLastCreatedElems.Clear();
8516   myLastCreatedNodes.Clear();
8517
8518   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8519   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8520     const SMDS_MeshElement* elem = invElemIt->next();
8521
8522     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8523     SMDS_VolumeTool aVolume (elem);
8524     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8525       continue;
8526
8527     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8528     int iface, nbFaces = aVolume.NbFaces();
8529     vector<const SMDS_MeshNode *> poly_nodes;
8530     vector<int> quantities (nbFaces);
8531
8532     for (iface = 0; iface < nbFaces; iface++) {
8533       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8534       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8535       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8536
8537       for (int inode = 0; inode < nbFaceNodes; inode++) {
8538         poly_nodes.push_back(faceNodes[inode]);
8539
8540         if (nbInserted == 0) {
8541           if (faceNodes[inode] == theBetweenNode1) {
8542             if (faceNodes[inode + 1] == theBetweenNode2) {
8543               nbInserted = theNodesToInsert.size();
8544
8545               // add nodes to insert
8546               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8547               for (; nIt != theNodesToInsert.end(); nIt++) {
8548                 poly_nodes.push_back(*nIt);
8549               }
8550             }
8551           }
8552           else if (faceNodes[inode] == theBetweenNode2) {
8553             if (faceNodes[inode + 1] == theBetweenNode1) {
8554               nbInserted = theNodesToInsert.size();
8555
8556               // add nodes to insert in reversed order
8557               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8558               nIt--;
8559               for (; nIt != theNodesToInsert.begin(); nIt--) {
8560                 poly_nodes.push_back(*nIt);
8561               }
8562               poly_nodes.push_back(*nIt);
8563             }
8564           }
8565           else {
8566           }
8567         }
8568       }
8569       quantities[iface] = nbFaceNodes + nbInserted;
8570     }
8571
8572     // Replace or update the volume
8573     SMESHDS_Mesh *aMesh = GetMeshDS();
8574
8575     if (elem->IsPoly()) {
8576       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8577
8578     }
8579     else {
8580       int aShapeId = FindShape( elem );
8581
8582       SMDS_MeshElement* newElem =
8583         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8584       myLastCreatedElems.Append(newElem);
8585       if (aShapeId && newElem)
8586         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8587
8588       aMesh->RemoveElement(elem);
8589     }
8590   }
8591 }
8592
8593 namespace
8594 {
8595   //================================================================================
8596   /*!
8597    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8598    */
8599   //================================================================================
8600
8601   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8602                            vector<const SMDS_MeshNode *> & nodes,
8603                            vector<int> &                   nbNodeInFaces )
8604   {
8605     nodes.clear();
8606     nbNodeInFaces.clear();
8607     SMDS_VolumeTool vTool ( elem );
8608     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8609     {
8610       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8611       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8612       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8613     }
8614   }
8615 }
8616
8617 //=======================================================================
8618 /*!
8619  * \brief Convert elements contained in a submesh to quadratic
8620  * \return int - nb of checked elements
8621  */
8622 //=======================================================================
8623
8624 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8625                                              SMESH_MesherHelper& theHelper,
8626                                              const bool          theForce3d)
8627 {
8628   int nbElem = 0;
8629   if( !theSm ) return nbElem;
8630
8631   vector<int> nbNodeInFaces;
8632   vector<const SMDS_MeshNode *> nodes;
8633   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8634   while(ElemItr->more())
8635   {
8636     nbElem++;
8637     const SMDS_MeshElement* elem = ElemItr->next();
8638     if( !elem ) continue;
8639
8640     // analyse a necessity of conversion
8641     const SMDSAbs_ElementType aType = elem->GetType();
8642     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8643       continue;
8644     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8645     bool hasCentralNodes = false;
8646     if ( elem->IsQuadratic() )
8647     {
8648       bool alreadyOK;
8649       switch ( aGeomType ) {
8650       case SMDSEntity_Quad_Triangle:
8651       case SMDSEntity_Quad_Quadrangle:
8652       case SMDSEntity_Quad_Hexa:
8653         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8654
8655       case SMDSEntity_BiQuad_Triangle:
8656       case SMDSEntity_BiQuad_Quadrangle:
8657       case SMDSEntity_TriQuad_Hexa:
8658         alreadyOK = theHelper.GetIsBiQuadratic();
8659         hasCentralNodes = true;
8660         break;
8661       default:
8662         alreadyOK = true;
8663       }
8664       // take into account already present modium nodes
8665       switch ( aType ) {
8666       case SMDSAbs_Volume:
8667         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8668       case SMDSAbs_Face:
8669         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8670       case SMDSAbs_Edge:
8671         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8672       default:;
8673       }
8674       if ( alreadyOK )
8675         continue;
8676     }
8677     // get elem data needed to re-create it
8678     //
8679     const int id      = elem->GetID();
8680     const int nbNodes = elem->NbCornerNodes();
8681     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8682     if ( aGeomType == SMDSEntity_Polyhedra )
8683       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8684     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8685       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8686
8687     // remove a linear element
8688     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8689
8690     // remove central nodes of biquadratic elements (biquad->quad convertion)
8691     if ( hasCentralNodes )
8692       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8693         if ( nodes[i]->NbInverseElements() == 0 )
8694           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8695
8696     const SMDS_MeshElement* NewElem = 0;
8697
8698     switch( aType )
8699     {
8700     case SMDSAbs_Edge :
8701       {
8702         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8703         break;
8704       }
8705     case SMDSAbs_Face :
8706       {
8707         switch(nbNodes)
8708         {
8709         case 3:
8710           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8711           break;
8712         case 4:
8713           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8714           break;
8715         default:
8716           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8717         }
8718         break;
8719       }
8720     case SMDSAbs_Volume :
8721       {
8722         switch( aGeomType )
8723         {
8724         case SMDSEntity_Tetra:
8725           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8726           break;
8727         case SMDSEntity_Pyramid:
8728           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8729           break;
8730         case SMDSEntity_Penta:
8731           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8732           break;
8733         case SMDSEntity_Hexa:
8734         case SMDSEntity_Quad_Hexa:
8735         case SMDSEntity_TriQuad_Hexa:
8736           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8737                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8738           break;
8739         case SMDSEntity_Hexagonal_Prism:
8740         default:
8741           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8742         }
8743         break;
8744       }
8745     default :
8746       continue;
8747     }
8748     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8749     if( NewElem && NewElem->getshapeId() < 1 )
8750       theSm->AddElement( NewElem );
8751   }
8752   return nbElem;
8753 }
8754 //=======================================================================
8755 //function : ConvertToQuadratic
8756 //purpose  :
8757 //=======================================================================
8758
8759 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8760 {
8761   SMESHDS_Mesh* meshDS = GetMeshDS();
8762
8763   SMESH_MesherHelper aHelper(*myMesh);
8764
8765   aHelper.SetIsQuadratic( true );
8766   aHelper.SetIsBiQuadratic( theToBiQuad );
8767   aHelper.SetElementsOnShape(true);
8768   aHelper.ToFixNodeParameters( true );
8769
8770   // convert elements assigned to sub-meshes
8771   int nbCheckedElems = 0;
8772   if ( myMesh->HasShapeToMesh() )
8773   {
8774     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8775     {
8776       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8777       while ( smIt->more() ) {
8778         SMESH_subMesh* sm = smIt->next();
8779         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8780           aHelper.SetSubShape( sm->GetSubShape() );
8781           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8782         }
8783       }
8784     }
8785   }
8786
8787   // convert elements NOT assigned to sub-meshes
8788   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8789   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8790   {
8791     aHelper.SetElementsOnShape(false);
8792     SMESHDS_SubMesh *smDS = 0;
8793
8794     // convert edges
8795     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8796     while( aEdgeItr->more() )
8797     {
8798       const SMDS_MeshEdge* edge = aEdgeItr->next();
8799       if ( !edge->IsQuadratic() )
8800       {
8801         int                  id = edge->GetID();
8802         const SMDS_MeshNode* n1 = edge->GetNode(0);
8803         const SMDS_MeshNode* n2 = edge->GetNode(1);
8804
8805         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8806
8807         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8808         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8809       }
8810       else
8811       {
8812         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8813       }
8814     }
8815
8816     // convert faces
8817     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8818     while( aFaceItr->more() )
8819     {
8820       const SMDS_MeshFace* face = aFaceItr->next();
8821       if ( !face ) continue;
8822       
8823       const SMDSAbs_EntityType type = face->GetEntityType();
8824       bool alreadyOK;
8825       switch( type )
8826       {
8827       case SMDSEntity_Quad_Triangle:
8828       case SMDSEntity_Quad_Quadrangle:
8829         alreadyOK = !theToBiQuad;
8830         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8831         break;
8832       case SMDSEntity_BiQuad_Triangle:
8833       case SMDSEntity_BiQuad_Quadrangle:
8834         alreadyOK = theToBiQuad;
8835         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8836         break;
8837       default: alreadyOK = false;
8838       }
8839       if ( alreadyOK )
8840         continue;
8841
8842       const int id = face->GetID();
8843       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8844
8845       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8846
8847       SMDS_MeshFace * NewFace = 0;
8848       switch( type )
8849       {
8850       case SMDSEntity_Triangle:
8851       case SMDSEntity_Quad_Triangle:
8852       case SMDSEntity_BiQuad_Triangle:
8853         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8854         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8855           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8856         break;
8857
8858       case SMDSEntity_Quadrangle:
8859       case SMDSEntity_Quad_Quadrangle:
8860       case SMDSEntity_BiQuad_Quadrangle:
8861         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8862         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8863           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8864         break;
8865
8866       default:;
8867         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8868       }
8869       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8870     }
8871
8872     // convert volumes
8873     vector<int> nbNodeInFaces;
8874     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8875     while(aVolumeItr->more())
8876     {
8877       const SMDS_MeshVolume* volume = aVolumeItr->next();
8878       if ( !volume ) continue;
8879
8880       const SMDSAbs_EntityType type = volume->GetEntityType();
8881       if ( volume->IsQuadratic() )
8882       {
8883         bool alreadyOK;
8884         switch ( type )
8885         {
8886         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8887         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8888         default:                      alreadyOK = true;
8889         }
8890         if ( alreadyOK )
8891         {
8892           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8893           continue;
8894         }
8895       }
8896       const int id = volume->GetID();
8897       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8898       if ( type == SMDSEntity_Polyhedra )
8899         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8900       else if ( type == SMDSEntity_Hexagonal_Prism )
8901         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8902
8903       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8904
8905       SMDS_MeshVolume * NewVolume = 0;
8906       switch ( type )
8907       {
8908       case SMDSEntity_Tetra:
8909         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8910         break;
8911       case SMDSEntity_Hexa:
8912       case SMDSEntity_Quad_Hexa:
8913       case SMDSEntity_TriQuad_Hexa:
8914         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8915                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8916         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8917           if ( nodes[i]->NbInverseElements() == 0 )
8918             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8919         break;
8920       case SMDSEntity_Pyramid:
8921         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8922                                       nodes[3], nodes[4], id, theForce3d);
8923         break;
8924       case SMDSEntity_Penta:
8925         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8926                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8927         break;
8928       case SMDSEntity_Hexagonal_Prism:
8929       default:
8930         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8931       }
8932       ReplaceElemInGroups(volume, NewVolume, meshDS);
8933     }
8934   }
8935
8936   if ( !theForce3d )
8937   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8938     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8939     // aHelper.FixQuadraticElements(myError);
8940     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8941   }
8942 }
8943
8944 //================================================================================
8945 /*!
8946  * \brief Makes given elements quadratic
8947  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
8948  *  \param theElements - elements to make quadratic
8949  */
8950 //================================================================================
8951
8952 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
8953                                           TIDSortedElemSet& theElements,
8954                                           const bool        theToBiQuad)
8955 {
8956   if ( theElements.empty() ) return;
8957
8958   // we believe that all theElements are of the same type
8959   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8960
8961   // get all nodes shared by theElements
8962   TIDSortedNodeSet allNodes;
8963   TIDSortedElemSet::iterator eIt = theElements.begin();
8964   for ( ; eIt != theElements.end(); ++eIt )
8965     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8966
8967   // complete theElements with elements of lower dim whose all nodes are in allNodes
8968
8969   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8970   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8971   TIDSortedNodeSet::iterator nIt = allNodes.begin();
8972   for ( ; nIt != allNodes.end(); ++nIt )
8973   {
8974     const SMDS_MeshNode* n = *nIt;
8975     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8976     while ( invIt->more() )
8977     {
8978       const SMDS_MeshElement*      e = invIt->next();
8979       const SMDSAbs_ElementType type = e->GetType();
8980       if ( e->IsQuadratic() )
8981       {
8982         quadAdjacentElems[ type ].insert( e );
8983
8984         bool alreadyOK;
8985         switch ( e->GetEntityType() ) {
8986         case SMDSEntity_Quad_Triangle:
8987         case SMDSEntity_Quad_Quadrangle:
8988         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
8989         case SMDSEntity_BiQuad_Triangle:
8990         case SMDSEntity_BiQuad_Quadrangle:
8991         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
8992         default:                           alreadyOK = true;
8993         }
8994         if ( alreadyOK )
8995           continue;
8996       }
8997       if ( type >= elemType )
8998         continue; // same type or more complex linear element
8999
9000       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9001         continue; // e is already checked
9002
9003       // check nodes
9004       bool allIn = true;
9005       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9006       while ( nodeIt->more() && allIn )
9007         allIn = allNodes.count( nodeIt->next() );
9008       if ( allIn )
9009         theElements.insert(e );
9010     }
9011   }
9012
9013   SMESH_MesherHelper helper(*myMesh);
9014   helper.SetIsQuadratic( true );
9015   helper.SetIsBiQuadratic( theToBiQuad );
9016
9017   // add links of quadratic adjacent elements to the helper
9018
9019   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9020     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9021           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9022     {
9023       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9024     }
9025   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9026     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9027           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9028     {
9029       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9030     }
9031   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9032     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9033           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9034     {
9035       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9036     }
9037
9038   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9039
9040   SMESHDS_Mesh*  meshDS = GetMeshDS();
9041   SMESHDS_SubMesh* smDS = 0;
9042   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9043   {
9044     const SMDS_MeshElement* elem = *eIt;
9045
9046     bool alreadyOK;
9047     int nbCentralNodes = 0;
9048     switch ( elem->GetEntityType() ) {
9049       // linear convertible
9050     case SMDSEntity_Edge:
9051     case SMDSEntity_Triangle:
9052     case SMDSEntity_Quadrangle:
9053     case SMDSEntity_Tetra:
9054     case SMDSEntity_Pyramid:
9055     case SMDSEntity_Hexa:
9056     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9057       // quadratic that can become bi-quadratic
9058     case SMDSEntity_Quad_Triangle:
9059     case SMDSEntity_Quad_Quadrangle:
9060     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9061       // bi-quadratic
9062     case SMDSEntity_BiQuad_Triangle:
9063     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9064     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9065       // the rest
9066     default:                           alreadyOK = true;
9067     }
9068     if ( alreadyOK ) continue;
9069
9070     const SMDSAbs_ElementType type = elem->GetType();
9071     const int                   id = elem->GetID();
9072     const int              nbNodes = elem->NbCornerNodes();
9073     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9074
9075     helper.SetSubShape( elem->getshapeId() );
9076
9077     if ( !smDS || !smDS->Contains( elem ))
9078       smDS = meshDS->MeshElements( elem->getshapeId() );
9079     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9080
9081     SMDS_MeshElement * newElem = 0;
9082     switch( nbNodes )
9083     {
9084     case 4: // cases for most frequently used element types go first (for optimization)
9085       if ( type == SMDSAbs_Volume )
9086         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9087       else
9088         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9089       break;
9090     case 8:
9091       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9092                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9093       break;
9094     case 3:
9095       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9096       break;
9097     case 2:
9098       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9099       break;
9100     case 5:
9101       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9102                                  nodes[4], id, theForce3d);
9103       break;
9104     case 6:
9105       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9106                                  nodes[4], nodes[5], id, theForce3d);
9107       break;
9108     default:;
9109     }
9110     ReplaceElemInGroups( elem, newElem, meshDS);
9111     if( newElem && smDS )
9112       smDS->AddElement( newElem );
9113
9114      // remove central nodes
9115     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9116       if ( nodes[i]->NbInverseElements() == 0 )
9117         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9118
9119   } // loop on theElements
9120
9121   if ( !theForce3d )
9122   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9123     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9124     // helper.FixQuadraticElements( myError );
9125     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9126   }
9127 }
9128
9129 //=======================================================================
9130 /*!
9131  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9132  * \return int - nb of checked elements
9133  */
9134 //=======================================================================
9135
9136 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9137                                      SMDS_ElemIteratorPtr theItr,
9138                                      const int            theShapeID)
9139 {
9140   int nbElem = 0;
9141   SMESHDS_Mesh* meshDS = GetMeshDS();
9142
9143   while( theItr->more() )
9144   {
9145     const SMDS_MeshElement* elem = theItr->next();
9146     nbElem++;
9147     if( elem && elem->IsQuadratic())
9148     {
9149       int id                    = elem->GetID();
9150       int nbCornerNodes         = elem->NbCornerNodes();
9151       SMDSAbs_ElementType aType = elem->GetType();
9152
9153       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9154
9155       //remove a quadratic element
9156       if ( !theSm || !theSm->Contains( elem ))
9157         theSm = meshDS->MeshElements( elem->getshapeId() );
9158       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9159
9160       // remove medium nodes
9161       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9162         if ( nodes[i]->NbInverseElements() == 0 )
9163           meshDS->RemoveFreeNode( nodes[i], theSm );
9164
9165       // add a linear element
9166       nodes.resize( nbCornerNodes );
9167       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9168       ReplaceElemInGroups(elem, newElem, meshDS);
9169       if( theSm && newElem )
9170         theSm->AddElement( newElem );
9171     }
9172   }
9173   return nbElem;
9174 }
9175
9176 //=======================================================================
9177 //function : ConvertFromQuadratic
9178 //purpose  :
9179 //=======================================================================
9180
9181 bool SMESH_MeshEditor::ConvertFromQuadratic()
9182 {
9183   int nbCheckedElems = 0;
9184   if ( myMesh->HasShapeToMesh() )
9185   {
9186     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9187     {
9188       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9189       while ( smIt->more() ) {
9190         SMESH_subMesh* sm = smIt->next();
9191         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9192           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9193       }
9194     }
9195   }
9196
9197   int totalNbElems =
9198     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9199   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9200   {
9201     SMESHDS_SubMesh *aSM = 0;
9202     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9203   }
9204
9205   return true;
9206 }
9207
9208 namespace
9209 {
9210   //================================================================================
9211   /*!
9212    * \brief Return true if all medium nodes of the element are in the node set
9213    */
9214   //================================================================================
9215
9216   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9217   {
9218     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9219       if ( !nodeSet.count( elem->GetNode(i) ))
9220         return false;
9221     return true;
9222   }
9223 }
9224
9225 //================================================================================
9226 /*!
9227  * \brief Makes given elements linear
9228  */
9229 //================================================================================
9230
9231 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9232 {
9233   if ( theElements.empty() ) return;
9234
9235   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9236   set<int> mediumNodeIDs;
9237   TIDSortedElemSet::iterator eIt = theElements.begin();
9238   for ( ; eIt != theElements.end(); ++eIt )
9239   {
9240     const SMDS_MeshElement* e = *eIt;
9241     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9242       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9243   }
9244
9245   // replace given elements by linear ones
9246   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9247   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9248
9249   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9250   // except those elements sharing medium nodes of quadratic element whose medium nodes
9251   // are not all in mediumNodeIDs
9252
9253   // get remaining medium nodes
9254   TIDSortedNodeSet mediumNodes;
9255   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9256   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9257     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9258       mediumNodes.insert( mediumNodes.end(), n );
9259
9260   // find more quadratic elements to convert
9261   TIDSortedElemSet moreElemsToConvert;
9262   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9263   for ( ; nIt != mediumNodes.end(); ++nIt )
9264   {
9265     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9266     while ( invIt->more() )
9267     {
9268       const SMDS_MeshElement* e = invIt->next();
9269       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9270       {
9271         // find a more complex element including e and
9272         // whose medium nodes are not in mediumNodes
9273         bool complexFound = false;
9274         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9275         {
9276           SMDS_ElemIteratorPtr invIt2 =
9277             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9278           while ( invIt2->more() )
9279           {
9280             const SMDS_MeshElement* eComplex = invIt2->next();
9281             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9282             {
9283               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9284               if ( nbCommonNodes == e->NbNodes())
9285               {
9286                 complexFound = true;
9287                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9288                 break;
9289               }
9290             }
9291           }
9292         }
9293         if ( !complexFound )
9294           moreElemsToConvert.insert( e );
9295       }
9296     }
9297   }
9298   elemIt = elemSetIterator( moreElemsToConvert );
9299   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9300 }
9301
9302 //=======================================================================
9303 //function : SewSideElements
9304 //purpose  :
9305 //=======================================================================
9306
9307 SMESH_MeshEditor::Sew_Error
9308 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9309                                    TIDSortedElemSet&    theSide2,
9310                                    const SMDS_MeshNode* theFirstNode1,
9311                                    const SMDS_MeshNode* theFirstNode2,
9312                                    const SMDS_MeshNode* theSecondNode1,
9313                                    const SMDS_MeshNode* theSecondNode2)
9314 {
9315   myLastCreatedElems.Clear();
9316   myLastCreatedNodes.Clear();
9317
9318   MESSAGE ("::::SewSideElements()");
9319   if ( theSide1.size() != theSide2.size() )
9320     return SEW_DIFF_NB_OF_ELEMENTS;
9321
9322   Sew_Error aResult = SEW_OK;
9323   // Algo:
9324   // 1. Build set of faces representing each side
9325   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9326   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9327
9328   // =======================================================================
9329   // 1. Build set of faces representing each side:
9330   // =======================================================================
9331   // a. build set of nodes belonging to faces
9332   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9333   // c. create temporary faces representing side of volumes if correspondent
9334   //    face does not exist
9335
9336   SMESHDS_Mesh* aMesh = GetMeshDS();
9337   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9338   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9339   TIDSortedElemSet             faceSet1, faceSet2;
9340   set<const SMDS_MeshElement*> volSet1,  volSet2;
9341   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9342   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9343   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9344   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9345   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9346   int iSide, iFace, iNode;
9347
9348   list<const SMDS_MeshElement* > tempFaceList;
9349   for ( iSide = 0; iSide < 2; iSide++ ) {
9350     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9351     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9352     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9353     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9354     set<const SMDS_MeshElement*>::iterator vIt;
9355     TIDSortedElemSet::iterator eIt;
9356     set<const SMDS_MeshNode*>::iterator    nIt;
9357
9358     // check that given nodes belong to given elements
9359     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9360     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9361     int firstIndex = -1, secondIndex = -1;
9362     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9363       const SMDS_MeshElement* elem = *eIt;
9364       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9365       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9366       if ( firstIndex > -1 && secondIndex > -1 ) break;
9367     }
9368     if ( firstIndex < 0 || secondIndex < 0 ) {
9369       // we can simply return until temporary faces created
9370       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9371     }
9372
9373     // -----------------------------------------------------------
9374     // 1a. Collect nodes of existing faces
9375     //     and build set of face nodes in order to detect missing
9376     //     faces corresponding to sides of volumes
9377     // -----------------------------------------------------------
9378
9379     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9380
9381     // loop on the given element of a side
9382     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9383       //const SMDS_MeshElement* elem = *eIt;
9384       const SMDS_MeshElement* elem = *eIt;
9385       if ( elem->GetType() == SMDSAbs_Face ) {
9386         faceSet->insert( elem );
9387         set <const SMDS_MeshNode*> faceNodeSet;
9388         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9389         while ( nodeIt->more() ) {
9390           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9391           nodeSet->insert( n );
9392           faceNodeSet.insert( n );
9393         }
9394         setOfFaceNodeSet.insert( faceNodeSet );
9395       }
9396       else if ( elem->GetType() == SMDSAbs_Volume )
9397         volSet->insert( elem );
9398     }
9399     // ------------------------------------------------------------------------------
9400     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9401     // ------------------------------------------------------------------------------
9402
9403     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9404       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9405       while ( fIt->more() ) { // loop on faces sharing a node
9406         const SMDS_MeshElement* f = fIt->next();
9407         if ( faceSet->find( f ) == faceSet->end() ) {
9408           // check if all nodes are in nodeSet and
9409           // complete setOfFaceNodeSet if they are
9410           set <const SMDS_MeshNode*> faceNodeSet;
9411           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9412           bool allInSet = true;
9413           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9414             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9415             if ( nodeSet->find( n ) == nodeSet->end() )
9416               allInSet = false;
9417             else
9418               faceNodeSet.insert( n );
9419           }
9420           if ( allInSet ) {
9421             faceSet->insert( f );
9422             setOfFaceNodeSet.insert( faceNodeSet );
9423           }
9424         }
9425       }
9426     }
9427
9428     // -------------------------------------------------------------------------
9429     // 1c. Create temporary faces representing sides of volumes if correspondent
9430     //     face does not exist
9431     // -------------------------------------------------------------------------
9432
9433     if ( !volSet->empty() ) {
9434       //int nodeSetSize = nodeSet->size();
9435
9436       // loop on given volumes
9437       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9438         SMDS_VolumeTool vol (*vIt);
9439         // loop on volume faces: find free faces
9440         // --------------------------------------
9441         list<const SMDS_MeshElement* > freeFaceList;
9442         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9443           if ( !vol.IsFreeFace( iFace ))
9444             continue;
9445           // check if there is already a face with same nodes in a face set
9446           const SMDS_MeshElement* aFreeFace = 0;
9447           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9448           int nbNodes = vol.NbFaceNodes( iFace );
9449           set <const SMDS_MeshNode*> faceNodeSet;
9450           vol.GetFaceNodes( iFace, faceNodeSet );
9451           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9452           if ( isNewFace ) {
9453             // no such a face is given but it still can exist, check it
9454             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9455             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9456           }
9457           if ( !aFreeFace ) {
9458             // create a temporary face
9459             if ( nbNodes == 3 ) {
9460               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9461               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9462             }
9463             else if ( nbNodes == 4 ) {
9464               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9465               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9466             }
9467             else {
9468               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9469               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9470               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9471             }
9472             if ( aFreeFace )
9473               tempFaceList.push_back( aFreeFace );
9474           }
9475
9476           if ( aFreeFace )
9477             freeFaceList.push_back( aFreeFace );
9478
9479         } // loop on faces of a volume
9480
9481         // choose one of several free faces of a volume
9482         // --------------------------------------------
9483         if ( freeFaceList.size() > 1 ) {
9484           // choose a face having max nb of nodes shared by other elems of a side
9485           int maxNbNodes = -1;
9486           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9487           while ( fIt != freeFaceList.end() ) { // loop on free faces
9488             int nbSharedNodes = 0;
9489             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9490             while ( nodeIt->more() ) { // loop on free face nodes
9491               const SMDS_MeshNode* n =
9492                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9493               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9494               while ( invElemIt->more() ) {
9495                 const SMDS_MeshElement* e = invElemIt->next();
9496                 nbSharedNodes += faceSet->count( e );
9497                 nbSharedNodes += elemSet->count( e );
9498               }
9499             }
9500             if ( nbSharedNodes > maxNbNodes ) {
9501               maxNbNodes = nbSharedNodes;
9502               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9503             }
9504             else if ( nbSharedNodes == maxNbNodes ) {
9505               fIt++;
9506             }
9507             else {
9508               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9509             }
9510           }
9511           if ( freeFaceList.size() > 1 )
9512           {
9513             // could not choose one face, use another way
9514             // choose a face most close to the bary center of the opposite side
9515             gp_XYZ aBC( 0., 0., 0. );
9516             set <const SMDS_MeshNode*> addedNodes;
9517             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9518             eIt = elemSet2->begin();
9519             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9520               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9521               while ( nodeIt->more() ) { // loop on free face nodes
9522                 const SMDS_MeshNode* n =
9523                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9524                 if ( addedNodes.insert( n ).second )
9525                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9526               }
9527             }
9528             aBC /= addedNodes.size();
9529             double minDist = DBL_MAX;
9530             fIt = freeFaceList.begin();
9531             while ( fIt != freeFaceList.end() ) { // loop on free faces
9532               double dist = 0;
9533               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9534               while ( nodeIt->more() ) { // loop on free face nodes
9535                 const SMDS_MeshNode* n =
9536                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9537                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9538                 dist += ( aBC - p ).SquareModulus();
9539               }
9540               if ( dist < minDist ) {
9541                 minDist = dist;
9542                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9543               }
9544               else
9545                 fIt = freeFaceList.erase( fIt++ );
9546             }
9547           }
9548         } // choose one of several free faces of a volume
9549
9550         if ( freeFaceList.size() == 1 ) {
9551           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9552           faceSet->insert( aFreeFace );
9553           // complete a node set with nodes of a found free face
9554           //           for ( iNode = 0; iNode < ; iNode++ )
9555           //             nodeSet->insert( fNodes[ iNode ] );
9556         }
9557
9558       } // loop on volumes of a side
9559
9560       //       // complete a set of faces if new nodes in a nodeSet appeared
9561       //       // ----------------------------------------------------------
9562       //       if ( nodeSetSize != nodeSet->size() ) {
9563       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9564       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9565       //           while ( fIt->more() ) { // loop on faces sharing a node
9566       //             const SMDS_MeshElement* f = fIt->next();
9567       //             if ( faceSet->find( f ) == faceSet->end() ) {
9568       //               // check if all nodes are in nodeSet and
9569       //               // complete setOfFaceNodeSet if they are
9570       //               set <const SMDS_MeshNode*> faceNodeSet;
9571       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9572       //               bool allInSet = true;
9573       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9574       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9575       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9576       //                   allInSet = false;
9577       //                 else
9578       //                   faceNodeSet.insert( n );
9579       //               }
9580       //               if ( allInSet ) {
9581       //                 faceSet->insert( f );
9582       //                 setOfFaceNodeSet.insert( faceNodeSet );
9583       //               }
9584       //             }
9585       //           }
9586       //         }
9587       //       }
9588     } // Create temporary faces, if there are volumes given
9589   } // loop on sides
9590
9591   if ( faceSet1.size() != faceSet2.size() ) {
9592     // delete temporary faces: they are in reverseElements of actual nodes
9593 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9594 //    while ( tmpFaceIt->more() )
9595 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9596 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9597 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9598 //      aMesh->RemoveElement(*tmpFaceIt);
9599     MESSAGE("Diff nb of faces");
9600     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9601   }
9602
9603   // ============================================================
9604   // 2. Find nodes to merge:
9605   //              bind a node to remove to a node to put instead
9606   // ============================================================
9607
9608   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9609   if ( theFirstNode1 != theFirstNode2 )
9610     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9611   if ( theSecondNode1 != theSecondNode2 )
9612     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9613
9614   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9615   set< long > linkIdSet; // links to process
9616   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9617
9618   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9619   list< NLink > linkList[2];
9620   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9621   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9622   // loop on links in linkList; find faces by links and append links
9623   // of the found faces to linkList
9624   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9625   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9626   {
9627     NLink link[] = { *linkIt[0], *linkIt[1] };
9628     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9629     if ( !linkIdSet.count( linkID ) )
9630       continue;
9631
9632     // by links, find faces in the face sets,
9633     // and find indices of link nodes in the found faces;
9634     // in a face set, there is only one or no face sharing a link
9635     // ---------------------------------------------------------------
9636
9637     const SMDS_MeshElement* face[] = { 0, 0 };
9638     vector<const SMDS_MeshNode*> fnodes[2];
9639     int iLinkNode[2][2];
9640     TIDSortedElemSet avoidSet;
9641     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9642       const SMDS_MeshNode* n1 = link[iSide].first;
9643       const SMDS_MeshNode* n2 = link[iSide].second;
9644       //cout << "Side " << iSide << " ";
9645       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9646       // find a face by two link nodes
9647       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9648                                                       *faceSetPtr[ iSide ], avoidSet,
9649                                                       &iLinkNode[iSide][0],
9650                                                       &iLinkNode[iSide][1] );
9651       if ( face[ iSide ])
9652       {
9653         //cout << " F " << face[ iSide]->GetID() <<endl;
9654         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9655         // put face nodes to fnodes
9656         if ( face[ iSide ]->IsQuadratic() )
9657         {
9658           // use interlaced nodes iterator
9659           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9660           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9661           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9662           while ( nIter->more() )
9663             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9664         }
9665         else
9666         {
9667           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9668                                   face[ iSide ]->end_nodes() );
9669         }
9670         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9671       }
9672     }
9673
9674     // check similarity of elements of the sides
9675     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9676       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9677       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9678         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9679       }
9680       else {
9681         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9682       }
9683       break; // do not return because it's necessary to remove tmp faces
9684     }
9685
9686     // set nodes to merge
9687     // -------------------
9688
9689     if ( face[0] && face[1] )  {
9690       const int nbNodes = face[0]->NbNodes();
9691       if ( nbNodes != face[1]->NbNodes() ) {
9692         MESSAGE("Diff nb of face nodes");
9693         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9694         break; // do not return because it s necessary to remove tmp faces
9695       }
9696       bool reverse[] = { false, false }; // order of nodes in the link
9697       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9698         // analyse link orientation in faces
9699         int i1 = iLinkNode[ iSide ][ 0 ];
9700         int i2 = iLinkNode[ iSide ][ 1 ];
9701         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9702       }
9703       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9704       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9705       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9706       {
9707         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9708                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9709       }
9710
9711       // add other links of the faces to linkList
9712       // -----------------------------------------
9713
9714       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9715         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9716         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9717         if ( !iter_isnew.second ) { // already in a set: no need to process
9718           linkIdSet.erase( iter_isnew.first );
9719         }
9720         else // new in set == encountered for the first time: add
9721         {
9722           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9723           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9724           linkList[0].push_back ( NLink( n1, n2 ));
9725           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9726         }
9727       }
9728     } // 2 faces found
9729
9730     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9731       break;
9732
9733   } // loop on link lists
9734
9735   if ( aResult == SEW_OK &&
9736        ( //linkIt[0] != linkList[0].end() ||
9737          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9738     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9739              " " << (faceSetPtr[1]->empty()));
9740     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9741   }
9742
9743   // ====================================================================
9744   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9745   // ====================================================================
9746
9747   // delete temporary faces
9748 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9749 //  while ( tmpFaceIt->more() )
9750 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9751   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9752   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9753     aMesh->RemoveElement(*tmpFaceIt);
9754
9755   if ( aResult != SEW_OK)
9756     return aResult;
9757
9758   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9759   // loop on nodes replacement map
9760   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9761   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9762     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9763       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9764       nodeIDsToRemove.push_back( nToRemove->GetID() );
9765       // loop on elements sharing nToRemove
9766       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9767       while ( invElemIt->more() ) {
9768         const SMDS_MeshElement* e = invElemIt->next();
9769         // get a new suite of nodes: make replacement
9770         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9771         vector< const SMDS_MeshNode*> nodes( nbNodes );
9772         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9773         while ( nIt->more() ) {
9774           const SMDS_MeshNode* n =
9775             static_cast<const SMDS_MeshNode*>( nIt->next() );
9776           nnIt = nReplaceMap.find( n );
9777           if ( nnIt != nReplaceMap.end() ) {
9778             nbReplaced++;
9779             n = (*nnIt).second;
9780           }
9781           nodes[ i++ ] = n;
9782         }
9783         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9784         //         elemIDsToRemove.push_back( e->GetID() );
9785         //       else
9786         if ( nbReplaced )
9787           {
9788             SMDSAbs_ElementType etyp = e->GetType();
9789             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9790             if (newElem)
9791               {
9792                 myLastCreatedElems.Append(newElem);
9793                 AddToSameGroups(newElem, e, aMesh);
9794                 int aShapeId = e->getshapeId();
9795                 if ( aShapeId )
9796                   {
9797                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
9798                   }
9799               }
9800             aMesh->RemoveElement(e);
9801           }
9802       }
9803     }
9804
9805   Remove( nodeIDsToRemove, true );
9806
9807   return aResult;
9808 }
9809
9810 //================================================================================
9811 /*!
9812  * \brief Find corresponding nodes in two sets of faces
9813  * \param theSide1 - first face set
9814  * \param theSide2 - second first face
9815  * \param theFirstNode1 - a boundary node of set 1
9816  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9817  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9818  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9819  * \param nReplaceMap - output map of corresponding nodes
9820  * \return bool  - is a success or not
9821  */
9822 //================================================================================
9823
9824 #ifdef _DEBUG_
9825 //#define DEBUG_MATCHING_NODES
9826 #endif
9827
9828 SMESH_MeshEditor::Sew_Error
9829 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9830                                     set<const SMDS_MeshElement*>& theSide2,
9831                                     const SMDS_MeshNode*          theFirstNode1,
9832                                     const SMDS_MeshNode*          theFirstNode2,
9833                                     const SMDS_MeshNode*          theSecondNode1,
9834                                     const SMDS_MeshNode*          theSecondNode2,
9835                                     TNodeNodeMap &                nReplaceMap)
9836 {
9837   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9838
9839   nReplaceMap.clear();
9840   if ( theFirstNode1 != theFirstNode2 )
9841     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9842   if ( theSecondNode1 != theSecondNode2 )
9843     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9844
9845   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9846   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9847
9848   list< NLink > linkList[2];
9849   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9850   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9851
9852   // loop on links in linkList; find faces by links and append links
9853   // of the found faces to linkList
9854   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9855   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9856     NLink link[] = { *linkIt[0], *linkIt[1] };
9857     if ( linkSet.find( link[0] ) == linkSet.end() )
9858       continue;
9859
9860     // by links, find faces in the face sets,
9861     // and find indices of link nodes in the found faces;
9862     // in a face set, there is only one or no face sharing a link
9863     // ---------------------------------------------------------------
9864
9865     const SMDS_MeshElement* face[] = { 0, 0 };
9866     list<const SMDS_MeshNode*> notLinkNodes[2];
9867     //bool reverse[] = { false, false }; // order of notLinkNodes
9868     int nbNodes[2];
9869     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9870     {
9871       const SMDS_MeshNode* n1 = link[iSide].first;
9872       const SMDS_MeshNode* n2 = link[iSide].second;
9873       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9874       set< const SMDS_MeshElement* > facesOfNode1;
9875       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9876       {
9877         // during a loop of the first node, we find all faces around n1,
9878         // during a loop of the second node, we find one face sharing both n1 and n2
9879         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9880         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9881         while ( fIt->more() ) { // loop on faces sharing a node
9882           const SMDS_MeshElement* f = fIt->next();
9883           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9884               ! facesOfNode1.insert( f ).second ) // f encounters twice
9885           {
9886             if ( face[ iSide ] ) {
9887               MESSAGE( "2 faces per link " );
9888               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9889             }
9890             face[ iSide ] = f;
9891             faceSet->erase( f );
9892
9893             // get not link nodes
9894             int nbN = f->NbNodes();
9895             if ( f->IsQuadratic() )
9896               nbN /= 2;
9897             nbNodes[ iSide ] = nbN;
9898             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9899             int i1 = f->GetNodeIndex( n1 );
9900             int i2 = f->GetNodeIndex( n2 );
9901             int iEnd = nbN, iBeg = -1, iDelta = 1;
9902             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9903             if ( reverse ) {
9904               std::swap( iEnd, iBeg ); iDelta = -1;
9905             }
9906             int i = i2;
9907             while ( true ) {
9908               i += iDelta;
9909               if ( i == iEnd ) i = iBeg + iDelta;
9910               if ( i == i1 ) break;
9911               nodes.push_back ( f->GetNode( i ) );
9912             }
9913           }
9914         }
9915       }
9916     }
9917     // check similarity of elements of the sides
9918     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9919       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9920       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9921         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9922       }
9923       else {
9924         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9925       }
9926     }
9927
9928     // set nodes to merge
9929     // -------------------
9930
9931     if ( face[0] && face[1] )  {
9932       if ( nbNodes[0] != nbNodes[1] ) {
9933         MESSAGE("Diff nb of face nodes");
9934         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9935       }
9936 #ifdef DEBUG_MATCHING_NODES
9937       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9938                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9939                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9940 #endif
9941       int nbN = nbNodes[0];
9942       {
9943         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9944         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9945         for ( int i = 0 ; i < nbN - 2; ++i ) {
9946 #ifdef DEBUG_MATCHING_NODES
9947           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9948 #endif
9949           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9950         }
9951       }
9952
9953       // add other links of the face 1 to linkList
9954       // -----------------------------------------
9955
9956       const SMDS_MeshElement* f0 = face[0];
9957       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9958       for ( int i = 0; i < nbN; i++ )
9959       {
9960         const SMDS_MeshNode* n2 = f0->GetNode( i );
9961         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9962           linkSet.insert( SMESH_TLink( n1, n2 ));
9963         if ( !iter_isnew.second ) { // already in a set: no need to process
9964           linkSet.erase( iter_isnew.first );
9965         }
9966         else // new in set == encountered for the first time: add
9967         {
9968 #ifdef DEBUG_MATCHING_NODES
9969           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9970                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9971 #endif
9972           linkList[0].push_back ( NLink( n1, n2 ));
9973           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9974         }
9975         n1 = n2;
9976       }
9977     } // 2 faces found
9978   } // loop on link lists
9979
9980   return SEW_OK;
9981 }
9982
9983 //================================================================================
9984 /*!
9985  * \brief Create elements equal (on same nodes) to given ones
9986  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
9987  *              elements of the uppest dimension are duplicated.
9988  */
9989 //================================================================================
9990
9991 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9992 {
9993   CrearLastCreated();
9994   SMESHDS_Mesh* mesh = GetMeshDS();
9995
9996   // get an element type and an iterator over elements
9997
9998   SMDSAbs_ElementType type;
9999   SMDS_ElemIteratorPtr elemIt;
10000   vector< const SMDS_MeshElement* > allElems;
10001   if ( theElements.empty() )
10002   {
10003     if ( mesh->NbNodes() == 0 )
10004       return;
10005     // get most complex type
10006     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10007       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10008       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10009     };
10010     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10011       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10012       {
10013         type = types[i];
10014         break;
10015       }
10016     // put all elements in the vector <allElems>
10017     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10018     elemIt = mesh->elementsIterator( type );
10019     while ( elemIt->more() )
10020       allElems.push_back( elemIt->next());
10021     elemIt = elemSetIterator( allElems );
10022   }
10023   else
10024   {
10025     type = (*theElements.begin())->GetType();
10026     elemIt = elemSetIterator( theElements );
10027   }
10028
10029   // duplicate elements
10030
10031   if ( type == SMDSAbs_Ball )
10032   {
10033     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10034     while ( elemIt->more() )
10035     {
10036       const SMDS_MeshElement* elem = elemIt->next();
10037       if ( elem->GetType() != SMDSAbs_Ball )
10038         continue;
10039       if (( elem = mesh->AddBall( elem->GetNode(0),
10040                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10041         myLastCreatedElems.Append( elem );
10042     }
10043   }
10044   else
10045   {
10046     vector< const SMDS_MeshNode* > nodes;
10047     while ( elemIt->more() )
10048     {
10049       const SMDS_MeshElement* elem = elemIt->next();
10050       if ( elem->GetType() != type )
10051         continue;
10052
10053       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10054
10055       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10056       {
10057         std::vector<int> quantities =
10058           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10059         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10060       }
10061       else
10062       {
10063         AddElement( nodes, type, elem->IsPoly() );
10064         elem = 0; // myLastCreatedElems is already filled
10065       }
10066       if ( elem )
10067         myLastCreatedElems.Append( elem );
10068     }
10069   }
10070 }
10071
10072 //================================================================================
10073 /*!
10074   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10075   \param theElems - the list of elements (edges or faces) to be replicated
10076   The nodes for duplication could be found from these elements
10077   \param theNodesNot - list of nodes to NOT replicate
10078   \param theAffectedElems - the list of elements (cells and edges) to which the
10079   replicated nodes should be associated to.
10080   \return TRUE if operation has been completed successfully, FALSE otherwise
10081 */
10082 //================================================================================
10083
10084 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10085                                     const TIDSortedElemSet& theNodesNot,
10086                                     const TIDSortedElemSet& theAffectedElems )
10087 {
10088   myLastCreatedElems.Clear();
10089   myLastCreatedNodes.Clear();
10090
10091   if ( theElems.size() == 0 )
10092     return false;
10093
10094   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10095   if ( !aMeshDS )
10096     return false;
10097
10098   bool res = false;
10099   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10100   // duplicate elements and nodes
10101   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10102   // replce nodes by duplications
10103   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10104   return res;
10105 }
10106
10107 //================================================================================
10108 /*!
10109   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10110   \param theMeshDS - mesh instance
10111   \param theElems - the elements replicated or modified (nodes should be changed)
10112   \param theNodesNot - nodes to NOT replicate
10113   \param theNodeNodeMap - relation of old node to new created node
10114   \param theIsDoubleElem - flag os to replicate element or modify
10115   \return TRUE if operation has been completed successfully, FALSE otherwise
10116 */
10117 //================================================================================
10118
10119 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10120                                     const TIDSortedElemSet& theElems,
10121                                     const TIDSortedElemSet& theNodesNot,
10122                                     std::map< const SMDS_MeshNode*,
10123                                     const SMDS_MeshNode* >& theNodeNodeMap,
10124                                     const bool theIsDoubleElem )
10125 {
10126   MESSAGE("doubleNodes");
10127   // iterate on through element and duplicate them (by nodes duplication)
10128   bool res = false;
10129   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10130   for ( ;  elemItr != theElems.end(); ++elemItr )
10131   {
10132     const SMDS_MeshElement* anElem = *elemItr;
10133     if (!anElem)
10134       continue;
10135
10136     bool isDuplicate = false;
10137     // duplicate nodes to duplicate element
10138     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10139     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10140     int ind = 0;
10141     while ( anIter->more() )
10142     {
10143
10144       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10145       SMDS_MeshNode* aNewNode = aCurrNode;
10146       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10147         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10148       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10149       {
10150         // duplicate node
10151         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10152         theNodeNodeMap[ aCurrNode ] = aNewNode;
10153         myLastCreatedNodes.Append( aNewNode );
10154       }
10155       isDuplicate |= (aCurrNode != aNewNode);
10156       newNodes[ ind++ ] = aNewNode;
10157     }
10158     if ( !isDuplicate )
10159       continue;
10160
10161     if ( theIsDoubleElem )
10162       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10163     else
10164       {
10165       MESSAGE("ChangeElementNodes");
10166       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10167       }
10168     res = true;
10169   }
10170   return res;
10171 }
10172
10173 //================================================================================
10174 /*!
10175   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10176   \param theNodes - identifiers of nodes to be doubled
10177   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10178          nodes. If list of element identifiers is empty then nodes are doubled but
10179          they not assigned to elements
10180   \return TRUE if operation has been completed successfully, FALSE otherwise
10181 */
10182 //================================================================================
10183
10184 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10185                                     const std::list< int >& theListOfModifiedElems )
10186 {
10187   MESSAGE("DoubleNodes");
10188   myLastCreatedElems.Clear();
10189   myLastCreatedNodes.Clear();
10190
10191   if ( theListOfNodes.size() == 0 )
10192     return false;
10193
10194   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10195   if ( !aMeshDS )
10196     return false;
10197
10198   // iterate through nodes and duplicate them
10199
10200   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10201
10202   std::list< int >::const_iterator aNodeIter;
10203   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10204   {
10205     int aCurr = *aNodeIter;
10206     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10207     if ( !aNode )
10208       continue;
10209
10210     // duplicate node
10211
10212     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10213     if ( aNewNode )
10214     {
10215       anOldNodeToNewNode[ aNode ] = aNewNode;
10216       myLastCreatedNodes.Append( aNewNode );
10217     }
10218   }
10219
10220   // Create map of new nodes for modified elements
10221
10222   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10223
10224   std::list< int >::const_iterator anElemIter;
10225   for ( anElemIter = theListOfModifiedElems.begin();
10226         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10227   {
10228     int aCurr = *anElemIter;
10229     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10230     if ( !anElem )
10231       continue;
10232
10233     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10234
10235     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10236     int ind = 0;
10237     while ( anIter->more() )
10238     {
10239       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10240       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10241       {
10242         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10243         aNodeArr[ ind++ ] = aNewNode;
10244       }
10245       else
10246         aNodeArr[ ind++ ] = aCurrNode;
10247     }
10248     anElemToNodes[ anElem ] = aNodeArr;
10249   }
10250
10251   // Change nodes of elements
10252
10253   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10254     anElemToNodesIter = anElemToNodes.begin();
10255   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10256   {
10257     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10258     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10259     if ( anElem )
10260       {
10261       MESSAGE("ChangeElementNodes");
10262       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10263       }
10264   }
10265
10266   return true;
10267 }
10268
10269 namespace {
10270
10271   //================================================================================
10272   /*!
10273   \brief Check if element located inside shape
10274   \return TRUE if IN or ON shape, FALSE otherwise
10275   */
10276   //================================================================================
10277
10278   template<class Classifier>
10279   bool isInside(const SMDS_MeshElement* theElem,
10280                 Classifier&             theClassifier,
10281                 const double            theTol)
10282   {
10283     gp_XYZ centerXYZ (0, 0, 0);
10284     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10285     while (aNodeItr->more())
10286       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10287
10288     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10289     theClassifier.Perform(aPnt, theTol);
10290     TopAbs_State aState = theClassifier.State();
10291     return (aState == TopAbs_IN || aState == TopAbs_ON );
10292   }
10293
10294   //================================================================================
10295   /*!
10296    * \brief Classifier of the 3D point on the TopoDS_Face
10297    *        with interaface suitable for isInside()
10298    */
10299   //================================================================================
10300
10301   struct _FaceClassifier
10302   {
10303     Extrema_ExtPS       _extremum;
10304     BRepAdaptor_Surface _surface;
10305     TopAbs_State        _state;
10306
10307     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10308     {
10309       _extremum.Initialize( _surface,
10310                             _surface.FirstUParameter(), _surface.LastUParameter(),
10311                             _surface.FirstVParameter(), _surface.LastVParameter(),
10312                             _surface.Tolerance(), _surface.Tolerance() );
10313     }
10314     void Perform(const gp_Pnt& aPnt, double theTol)
10315     {
10316       _state = TopAbs_OUT;
10317       _extremum.Perform(aPnt);
10318       if ( _extremum.IsDone() )
10319         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10320 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10321           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10322 #else
10323           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10324 #endif
10325     }
10326     TopAbs_State State() const
10327     {
10328       return _state;
10329     }
10330   };
10331 }
10332
10333 //================================================================================
10334 /*!
10335   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10336   This method is the first step of DoubleNodeElemGroupsInRegion.
10337   \param theElems - list of groups of elements (edges or faces) to be replicated
10338   \param theNodesNot - list of groups of nodes not to replicated
10339   \param theShape - shape to detect affected elements (element which geometric center
10340          located on or inside shape). If the shape is null, detection is done on faces orientations
10341          (select elements with a gravity center on the side given by faces normals).
10342          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10343          The replicated nodes should be associated to affected elements.
10344   \return groups of affected elements
10345   \sa DoubleNodeElemGroupsInRegion()
10346  */
10347 //================================================================================
10348
10349 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10350                                                    const TIDSortedElemSet& theNodesNot,
10351                                                    const TopoDS_Shape&     theShape,
10352                                                    TIDSortedElemSet&       theAffectedElems)
10353 {
10354   if ( theShape.IsNull() )
10355   {
10356     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10357     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10358     std::set<const SMDS_MeshElement*> edgesToCheck;
10359     alreadyCheckedNodes.clear();
10360     alreadyCheckedElems.clear();
10361     edgesToCheck.clear();
10362
10363     // --- iterates on elements to be replicated and get elements by back references from their nodes
10364
10365     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10366     int ielem;
10367     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10368     {
10369       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10370       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10371         continue;
10372       gp_XYZ normal;
10373       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10374       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10375       std::set<const SMDS_MeshNode*> nodesElem;
10376       nodesElem.clear();
10377       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10378       while ( nodeItr->more() )
10379       {
10380         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10381         nodesElem.insert(aNode);
10382       }
10383       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10384       for (; nodit != nodesElem.end(); nodit++)
10385       {
10386         MESSAGE("  noeud ");
10387         const SMDS_MeshNode* aNode = *nodit;
10388         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10389           continue;
10390         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10391           continue;
10392         alreadyCheckedNodes.insert(aNode);
10393         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10394         while ( backElemItr->more() )
10395         {
10396           MESSAGE("    backelem ");
10397           const SMDS_MeshElement* curElem = backElemItr->next();
10398           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10399             continue;
10400           if (theElems.find(curElem) != theElems.end())
10401             continue;
10402           alreadyCheckedElems.insert(curElem);
10403           double x=0, y=0, z=0;
10404           int nb = 0;
10405           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10406           while ( nodeItr2->more() )
10407           {
10408             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10409             x += anotherNode->X();
10410             y += anotherNode->Y();
10411             z += anotherNode->Z();
10412             nb++;
10413           }
10414           gp_XYZ p;
10415           p.SetCoord( x/nb -aNode->X(),
10416                       y/nb -aNode->Y(),
10417                       z/nb -aNode->Z() );
10418           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10419           if (normal*p > 0)
10420           {
10421             MESSAGE("    --- inserted")
10422             theAffectedElems.insert( curElem );
10423           }
10424           else if (curElem->GetType() == SMDSAbs_Edge)
10425             edgesToCheck.insert(curElem);
10426         }
10427       }
10428     }
10429     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10430     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10431     for( ; eit != edgesToCheck.end(); eit++)
10432     {
10433       bool onside = true;
10434       const SMDS_MeshElement* anEdge = *eit;
10435       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10436       while ( nodeItr->more() )
10437       {
10438         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10439         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10440         {
10441           onside = false;
10442           break;
10443         }
10444       }
10445       if (onside)
10446       {
10447         MESSAGE("    --- edge onside inserted")
10448         theAffectedElems.insert(anEdge);
10449       }
10450     }
10451   }
10452   else
10453   {
10454     const double aTol = Precision::Confusion();
10455     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10456     auto_ptr<_FaceClassifier>              aFaceClassifier;
10457     if ( theShape.ShapeType() == TopAbs_SOLID )
10458     {
10459       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10460       bsc3d->PerformInfinitePoint(aTol);
10461     }
10462     else if (theShape.ShapeType() == TopAbs_FACE )
10463     {
10464       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10465     }
10466
10467     // iterates on indicated elements and get elements by back references from their nodes
10468     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10469     int ielem;
10470     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10471     {
10472       MESSAGE("element " << ielem++);
10473       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10474       if (!anElem)
10475         continue;
10476       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10477       while ( nodeItr->more() )
10478       {
10479         MESSAGE("  noeud ");
10480         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10481         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10482           continue;
10483         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10484         while ( backElemItr->more() )
10485         {
10486           MESSAGE("    backelem ");
10487           const SMDS_MeshElement* curElem = backElemItr->next();
10488           if ( curElem && theElems.find(curElem) == theElems.end() &&
10489               ( bsc3d.get() ?
10490                 isInside( curElem, *bsc3d, aTol ) :
10491                 isInside( curElem, *aFaceClassifier, aTol )))
10492             theAffectedElems.insert( curElem );
10493         }
10494       }
10495     }
10496   }
10497   return true;
10498 }
10499
10500 //================================================================================
10501 /*!
10502   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10503   \param theElems - group of of elements (edges or faces) to be replicated
10504   \param theNodesNot - group of nodes not to replicate
10505   \param theShape - shape to detect affected elements (element which geometric center
10506   located on or inside shape).
10507   The replicated nodes should be associated to affected elements.
10508   \return TRUE if operation has been completed successfully, FALSE otherwise
10509 */
10510 //================================================================================
10511
10512 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10513                                             const TIDSortedElemSet& theNodesNot,
10514                                             const TopoDS_Shape&     theShape )
10515 {
10516   if ( theShape.IsNull() )
10517     return false;
10518
10519   const double aTol = Precision::Confusion();
10520   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10521   auto_ptr<_FaceClassifier>              aFaceClassifier;
10522   if ( theShape.ShapeType() == TopAbs_SOLID )
10523   {
10524     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10525     bsc3d->PerformInfinitePoint(aTol);
10526   }
10527   else if (theShape.ShapeType() == TopAbs_FACE )
10528   {
10529     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10530   }
10531
10532   // iterates on indicated elements and get elements by back references from their nodes
10533   TIDSortedElemSet anAffected;
10534   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10535   for ( ;  elemItr != theElems.end(); ++elemItr )
10536   {
10537     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10538     if (!anElem)
10539       continue;
10540
10541     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10542     while ( nodeItr->more() )
10543     {
10544       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10545       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10546         continue;
10547       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10548       while ( backElemItr->more() )
10549       {
10550         const SMDS_MeshElement* curElem = backElemItr->next();
10551         if ( curElem && theElems.find(curElem) == theElems.end() &&
10552              ( bsc3d.get() ?
10553                isInside( curElem, *bsc3d, aTol ) :
10554                isInside( curElem, *aFaceClassifier, aTol )))
10555           anAffected.insert( curElem );
10556       }
10557     }
10558   }
10559   return DoubleNodes( theElems, theNodesNot, anAffected );
10560 }
10561
10562 /*!
10563  *  \brief compute an oriented angle between two planes defined by four points.
10564  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10565  *  @param p0 base of the rotation axe
10566  *  @param p1 extremity of the rotation axe
10567  *  @param g1 belongs to the first plane
10568  *  @param g2 belongs to the second plane
10569  */
10570 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10571 {
10572 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10573 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10574 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10575 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10576   gp_Vec vref(p0, p1);
10577   gp_Vec v1(p0, g1);
10578   gp_Vec v2(p0, g2);
10579   gp_Vec n1 = vref.Crossed(v1);
10580   gp_Vec n2 = vref.Crossed(v2);
10581   try {
10582     return n2.AngleWithRef(n1, vref);
10583   }
10584   catch ( Standard_Failure ) {
10585   }
10586   return Max( v1.Magnitude(), v2.Magnitude() );
10587 }
10588
10589 /*!
10590  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10591  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10592  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10593  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10594  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10595  * the group j_n_p is the group of the flat elements that are built between the group #n and the group #p in the list.
10596  * If there is no shared faces between the group #n and the group #p in the list, the group j_n_p is not created.
10597  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10598  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10599  * \param theElems - list of groups of volumes, where a group of volume is a set of
10600  *        SMDS_MeshElements sorted by Id.
10601  * \param createJointElems - if TRUE, create the elements
10602  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10603  *        the boundary between \a theDomains and the rest mesh
10604  * \return TRUE if operation has been completed successfully, FALSE otherwise
10605  */
10606 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10607                                                      bool                                 createJointElems,
10608                                                      bool                                 onAllBoundaries)
10609 {
10610   MESSAGE("----------------------------------------------");
10611   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10612   MESSAGE("----------------------------------------------");
10613
10614   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10615   meshDS->BuildDownWardConnectivity(true);
10616   CHRONO(50);
10617   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10618
10619   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10620   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10621   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10622
10623   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10624   std::map<int,int>celldom; // cell vtkId --> domain
10625   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10626   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10627   faceDomains.clear();
10628   celldom.clear();
10629   cellDomains.clear();
10630   nodeDomains.clear();
10631   std::map<int,int> emptyMap;
10632   std::set<int> emptySet;
10633   emptyMap.clear();
10634
10635   MESSAGE(".. Number of domains :"<<theElems.size());
10636
10637   TIDSortedElemSet theRestDomElems;
10638   const int iRestDom  = -1;
10639   const int idom0     = onAllBoundaries ? iRestDom : 0;
10640   const int nbDomains = theElems.size();
10641
10642   // Check if the domains do not share an element
10643   for (int idom = 0; idom < nbDomains-1; idom++)
10644     {
10645 //       MESSAGE("... Check of domain #" << idom);
10646       const TIDSortedElemSet& domain = theElems[idom];
10647       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10648       for (; elemItr != domain.end(); ++elemItr)
10649         {
10650           const SMDS_MeshElement* anElem = *elemItr;
10651           int idombisdeb = idom + 1 ;
10652           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10653           {
10654             const TIDSortedElemSet& domainbis = theElems[idombis];
10655             if ( domainbis.count(anElem) )
10656             {
10657               MESSAGE(".... Domain #" << idom);
10658               MESSAGE(".... Domain #" << idombis);
10659               throw SALOME_Exception("The domains are not disjoint.");
10660               return false ;
10661             }
10662           }
10663         }
10664     }
10665
10666   for (int idom = 0; idom < nbDomains; idom++)
10667     {
10668
10669       // --- build a map (face to duplicate --> volume to modify)
10670       //     with all the faces shared by 2 domains (group of elements)
10671       //     and corresponding volume of this domain, for each shared face.
10672       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10673
10674       MESSAGE("... Neighbors of domain #" << idom);
10675       const TIDSortedElemSet& domain = theElems[idom];
10676       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10677       for (; elemItr != domain.end(); ++elemItr)
10678         {
10679           const SMDS_MeshElement* anElem = *elemItr;
10680           if (!anElem)
10681             continue;
10682           int vtkId = anElem->getVtkId();
10683           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
10684           int neighborsVtkIds[NBMAXNEIGHBORS];
10685           int downIds[NBMAXNEIGHBORS];
10686           unsigned char downTypes[NBMAXNEIGHBORS];
10687           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10688           for (int n = 0; n < nbNeighbors; n++)
10689             {
10690               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10691               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10692               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10693                 {
10694                   bool ok = false ;
10695                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10696                   {
10697                     // MESSAGE("Domain " << idombis);
10698                     const TIDSortedElemSet& domainbis = theElems[idombis];
10699                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10700                   }
10701                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10702                   {
10703                     DownIdType face(downIds[n], downTypes[n]);
10704                     if (!faceDomains[face].count(idom))
10705                       {
10706                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10707                         celldom[vtkId] = idom;
10708                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
10709                       }
10710                     if ( !ok )
10711                     {
10712                       theRestDomElems.insert( elem );
10713                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
10714                       celldom[neighborsVtkIds[n]] = iRestDom;
10715                     }
10716                   }
10717                 }
10718             }
10719         }
10720     }
10721
10722   //MESSAGE("Number of shared faces " << faceDomains.size());
10723   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10724
10725   // --- explore the shared faces domain by domain,
10726   //     explore the nodes of the face and see if they belong to a cell in the domain,
10727   //     which has only a node or an edge on the border (not a shared face)
10728
10729   for (int idomain = idom0; idomain < nbDomains; idomain++)
10730     {
10731       //MESSAGE("Domain " << idomain);
10732       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10733       itface = faceDomains.begin();
10734       for (; itface != faceDomains.end(); ++itface)
10735         {
10736           const std::map<int, int>& domvol = itface->second;
10737           if (!domvol.count(idomain))
10738             continue;
10739           DownIdType face = itface->first;
10740           //MESSAGE(" --- face " << face.cellId);
10741           std::set<int> oldNodes;
10742           oldNodes.clear();
10743           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10744           std::set<int>::iterator itn = oldNodes.begin();
10745           for (; itn != oldNodes.end(); ++itn)
10746             {
10747               int oldId = *itn;
10748               //MESSAGE("     node " << oldId);
10749               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10750               for (int i=0; i<l.ncells; i++)
10751                 {
10752                   int vtkId = l.cells[i];
10753                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10754                   if (!domain.count(anElem))
10755                     continue;
10756                   int vtkType = grid->GetCellType(vtkId);
10757                   int downId = grid->CellIdToDownId(vtkId);
10758                   if (downId < 0)
10759                     {
10760                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10761                       continue; // not OK at this stage of the algorithm:
10762                                 //no cells created after BuildDownWardConnectivity
10763                     }
10764                   DownIdType aCell(downId, vtkType);
10765                   cellDomains[aCell][idomain] = vtkId;
10766                   celldom[vtkId] = idomain;
10767                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
10768                 }
10769             }
10770         }
10771     }
10772
10773   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10774   //     for each shared face, get the nodes
10775   //     for each node, for each domain of the face, create a clone of the node
10776
10777   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10778   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10779   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
10780
10781   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10782   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10783   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10784
10785   MESSAGE(".. Duplication of the nodes");
10786   for (int idomain = idom0; idomain < nbDomains; idomain++)
10787     {
10788       itface = faceDomains.begin();
10789       for (; itface != faceDomains.end(); ++itface)
10790         {
10791           const std::map<int, int>& domvol = itface->second;
10792           if (!domvol.count(idomain))
10793             continue;
10794           DownIdType face = itface->first;
10795           //MESSAGE(" --- face " << face.cellId);
10796           std::set<int> oldNodes;
10797           oldNodes.clear();
10798           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10799           std::set<int>::iterator itn = oldNodes.begin();
10800           for (; itn != oldNodes.end(); ++itn)
10801             {
10802               int oldId = *itn;
10803               if (nodeDomains[oldId].empty())
10804                 {
10805                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10806                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
10807                 }
10808               std::map<int, int>::const_iterator itdom = domvol.begin();
10809               for (; itdom != domvol.end(); ++itdom)
10810                 {
10811                   int idom = itdom->first;
10812                   //MESSAGE("         domain " << idom);
10813                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
10814                     {
10815                       if (nodeDomains[oldId].size() >= 2) // a multiple node
10816                         {
10817                           vector<int> orderedDoms;
10818                           //MESSAGE("multiple node " << oldId);
10819                           if (mutipleNodes.count(oldId))
10820                             orderedDoms = mutipleNodes[oldId];
10821                           else
10822                             {
10823                               map<int,int>::iterator it = nodeDomains[oldId].begin();
10824                               for (; it != nodeDomains[oldId].end(); ++it)
10825                                 orderedDoms.push_back(it->first);
10826                             }
10827                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
10828                           //stringstream txt;
10829                           //for (int i=0; i<orderedDoms.size(); i++)
10830                           //  txt << orderedDoms[i] << " ";
10831                           //MESSAGE("orderedDoms " << txt.str());
10832                           mutipleNodes[oldId] = orderedDoms;
10833                         }
10834                       double *coords = grid->GetPoint(oldId);
10835                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10836                       int newId = newNode->getVtkId();
10837                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
10838                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10839                     }
10840                 }
10841             }
10842         }
10843     }
10844
10845   MESSAGE(".. Creation of elements");
10846   for (int idomain = idom0; idomain < nbDomains; idomain++)
10847     {
10848       itface = faceDomains.begin();
10849       for (; itface != faceDomains.end(); ++itface)
10850         {
10851           std::map<int, int> domvol = itface->second;
10852           if (!domvol.count(idomain))
10853             continue;
10854           DownIdType face = itface->first;
10855           //MESSAGE(" --- face " << face.cellId);
10856           std::set<int> oldNodes;
10857           oldNodes.clear();
10858           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10859           int nbMultipleNodes = 0;
10860           std::set<int>::iterator itn = oldNodes.begin();
10861           for (; itn != oldNodes.end(); ++itn)
10862             {
10863               int oldId = *itn;
10864               if (mutipleNodes.count(oldId))
10865                 nbMultipleNodes++;
10866             }
10867           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10868             {
10869               //MESSAGE("multiple Nodes detected on a shared face");
10870               int downId = itface->first.cellId;
10871               unsigned char cellType = itface->first.cellType;
10872               // --- shared edge or shared face ?
10873               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10874                 {
10875                   int nodes[3];
10876                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10877                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10878                     if (mutipleNodes.count(nodes[i]))
10879                       if (!mutipleNodesToFace.count(nodes[i]))
10880                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10881                 }
10882               else // shared face (between two volumes)
10883                 {
10884                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10885                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10886                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10887                   for (int ie =0; ie < nbEdges; ie++)
10888                     {
10889                       int nodes[3];
10890                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10891                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10892                         {
10893                           vector<int> vn0 = mutipleNodes[nodes[0]];
10894                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10895                           vector<int> doms;
10896                           for (int i0 = 0; i0 < vn0.size(); i0++)
10897                             for (int i1 = 0; i1 < vn1.size(); i1++)
10898                               if (vn0[i0] == vn1[i1])
10899                                 doms.push_back(vn0[i0]);
10900                           if (doms.size() >2)
10901                             {
10902                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10903                               double *coords = grid->GetPoint(nodes[0]);
10904                               gp_Pnt p0(coords[0], coords[1], coords[2]);
10905                               coords = grid->GetPoint(nodes[nbNodes - 1]);
10906                               gp_Pnt p1(coords[0], coords[1], coords[2]);
10907                               gp_Pnt gref;
10908                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
10909                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10910                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10911                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10912                               for (int id=0; id < doms.size(); id++)
10913                                 {
10914                                   int idom = doms[id];
10915                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10916                                   for (int ivol=0; ivol<nbvol; ivol++)
10917                                     {
10918                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10919                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10920                                       if (domain.count(elem))
10921                                         {
10922                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10923                                           domvol[idom] = svol;
10924                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
10925                                           double values[3];
10926                                           vtkIdType npts = 0;
10927                                           vtkIdType* pts = 0;
10928                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10929                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10930                                           if (id ==0)
10931                                             {
10932                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10933                                               angleDom[idom] = 0;
10934                                             }
10935                                           else
10936                                             {
10937                                               gp_Pnt g(values[0], values[1], values[2]);
10938                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10939                                               //MESSAGE("  angle=" << angleDom[idom]);
10940                                             }
10941                                           break;
10942                                         }
10943                                     }
10944                                 }
10945                               map<double, int> sortedDom; // sort domains by angle
10946                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10947                                 sortedDom[ia->second] = ia->first;
10948                               vector<int> vnodes;
10949                               vector<int> vdom;
10950                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10951                                 {
10952                                   vdom.push_back(ib->second);
10953                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
10954                                 }
10955                               for (int ino = 0; ino < nbNodes; ino++)
10956                                 vnodes.push_back(nodes[ino]);
10957                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10958                             }
10959                         }
10960                     }
10961                 }
10962             }
10963         }
10964     }
10965
10966   // --- iterate on shared faces (volumes to modify, face to extrude)
10967   //     get node id's of the face (id SMDS = id VTK)
10968   //     create flat element with old and new nodes if requested
10969
10970   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10971   //     (domain1 X domain2) = domain1 + MAXINT*domain2
10972
10973   std::map<int, std::map<long,int> > nodeQuadDomains;
10974   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10975
10976   MESSAGE(".. Creation of elements: simple junction");
10977   if (createJointElems)
10978     {
10979       int idg;
10980       string joints2DName = "joints2D";
10981       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10982       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10983       string joints3DName = "joints3D";
10984       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10985       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10986
10987       itface = faceDomains.begin();
10988       for (; itface != faceDomains.end(); ++itface)
10989         {
10990           DownIdType face = itface->first;
10991           std::set<int> oldNodes;
10992           std::set<int>::iterator itn;
10993           oldNodes.clear();
10994           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10995
10996           std::map<int, int> domvol = itface->second;
10997           std::map<int, int>::iterator itdom = domvol.begin();
10998           int dom1 = itdom->first;
10999           int vtkVolId = itdom->second;
11000           itdom++;
11001           int dom2 = itdom->first;
11002           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11003                                                              nodeQuadDomains);
11004           stringstream grpname;
11005           grpname << "j_";
11006           if (dom1 < dom2)
11007             grpname << dom1 << "_" << dom2;
11008           else
11009             grpname << dom2 << "_" << dom1;
11010           string namegrp = grpname.str();
11011           if (!mapOfJunctionGroups.count(namegrp))
11012             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11013           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11014           if (sgrp)
11015             sgrp->Add(vol->GetID());
11016           if (vol->GetType() == SMDSAbs_Volume)
11017             joints3DGrp->Add(vol->GetID());
11018           else if (vol->GetType() == SMDSAbs_Face)
11019             joints2DGrp->Add(vol->GetID());
11020         }
11021     }
11022
11023   // --- create volumes on multiple domain intersection if requested
11024   //     iterate on mutipleNodesToFace
11025   //     iterate on edgesMultiDomains
11026
11027   MESSAGE(".. Creation of elements: multiple junction");
11028   if (createJointElems)
11029     {
11030       // --- iterate on mutipleNodesToFace
11031
11032       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11033       for (; itn != mutipleNodesToFace.end(); ++itn)
11034         {
11035           int node = itn->first;
11036           vector<int> orderDom = itn->second;
11037           vector<vtkIdType> orderedNodes;
11038           for (int idom = 0; idom <orderDom.size(); idom++)
11039             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11040             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11041
11042             stringstream grpname;
11043             grpname << "m2j_";
11044             grpname << 0 << "_" << 0;
11045             int idg;
11046             string namegrp = grpname.str();
11047             if (!mapOfJunctionGroups.count(namegrp))
11048               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11049             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11050             if (sgrp)
11051               sgrp->Add(face->GetID());
11052         }
11053
11054       // --- iterate on edgesMultiDomains
11055
11056       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11057       for (; ite != edgesMultiDomains.end(); ++ite)
11058         {
11059           vector<int> nodes = ite->first;
11060           vector<int> orderDom = ite->second;
11061           vector<vtkIdType> orderedNodes;
11062           if (nodes.size() == 2)
11063             {
11064               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11065               for (int ino=0; ino < nodes.size(); ino++)
11066                 if (orderDom.size() == 3)
11067                   for (int idom = 0; idom <orderDom.size(); idom++)
11068                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11069                 else
11070                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11071                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11072               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11073
11074               int idg;
11075               string namegrp = "jointsMultiples";
11076               if (!mapOfJunctionGroups.count(namegrp))
11077                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11078               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11079               if (sgrp)
11080                 sgrp->Add(vol->GetID());
11081             }
11082           else
11083             {
11084               INFOS("Quadratic multiple joints not implemented");
11085               // TODO quadratic nodes
11086             }
11087         }
11088     }
11089
11090   // --- list the explicit faces and edges of the mesh that need to be modified,
11091   //     i.e. faces and edges built with one or more duplicated nodes.
11092   //     associate these faces or edges to their corresponding domain.
11093   //     only the first domain found is kept when a face or edge is shared
11094
11095   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11096   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11097   faceOrEdgeDom.clear();
11098   feDom.clear();
11099
11100   MESSAGE(".. Modification of elements");
11101   for (int idomain = idom0; idomain < nbDomains; idomain++)
11102     {
11103       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11104       for (; itnod != nodeDomains.end(); ++itnod)
11105         {
11106           int oldId = itnod->first;
11107           //MESSAGE("     node " << oldId);
11108           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11109           for (int i = 0; i < l.ncells; i++)
11110             {
11111               int vtkId = l.cells[i];
11112               int vtkType = grid->GetCellType(vtkId);
11113               int downId = grid->CellIdToDownId(vtkId);
11114               if (downId < 0)
11115                 continue; // new cells: not to be modified
11116               DownIdType aCell(downId, vtkType);
11117               int volParents[1000];
11118               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11119               for (int j = 0; j < nbvol; j++)
11120                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11121                   if (!feDom.count(vtkId))
11122                     {
11123                       feDom[vtkId] = idomain;
11124                       faceOrEdgeDom[aCell] = emptyMap;
11125                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11126                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11127                       //        << " type " << vtkType << " downId " << downId);
11128                     }
11129             }
11130         }
11131     }
11132
11133   // --- iterate on shared faces (volumes to modify, face to extrude)
11134   //     get node id's of the face
11135   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11136
11137   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11138   for (int m=0; m<3; m++)
11139     {
11140       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11141       itface = (*amap).begin();
11142       for (; itface != (*amap).end(); ++itface)
11143         {
11144           DownIdType face = itface->first;
11145           std::set<int> oldNodes;
11146           std::set<int>::iterator itn;
11147           oldNodes.clear();
11148           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11149           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11150           std::map<int, int> localClonedNodeIds;
11151
11152           std::map<int, int> domvol = itface->second;
11153           std::map<int, int>::iterator itdom = domvol.begin();
11154           for (; itdom != domvol.end(); ++itdom)
11155             {
11156               int idom = itdom->first;
11157               int vtkVolId = itdom->second;
11158               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11159               localClonedNodeIds.clear();
11160               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11161                 {
11162                   int oldId = *itn;
11163                   if (nodeDomains[oldId].count(idom))
11164                     {
11165                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11166                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11167                     }
11168                 }
11169               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11170             }
11171         }
11172     }
11173
11174   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11175   grid->BuildLinks();
11176
11177   CHRONOSTOP(50);
11178   counters::stats();
11179   return true;
11180 }
11181
11182 /*!
11183  * \brief Double nodes on some external faces and create flat elements.
11184  * Flat elements are mainly used by some types of mechanic calculations.
11185  *
11186  * Each group of the list must be constituted of faces.
11187  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11188  * @param theElems - list of groups of faces, where a group of faces is a set of
11189  * SMDS_MeshElements sorted by Id.
11190  * @return TRUE if operation has been completed successfully, FALSE otherwise
11191  */
11192 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11193 {
11194   MESSAGE("-------------------------------------------------");
11195   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11196   MESSAGE("-------------------------------------------------");
11197
11198   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11199
11200   // --- For each group of faces
11201   //     duplicate the nodes, create a flat element based on the face
11202   //     replace the nodes of the faces by their clones
11203
11204   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11205   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11206   clonedNodes.clear();
11207   intermediateNodes.clear();
11208   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11209   mapOfJunctionGroups.clear();
11210
11211   for (int idom = 0; idom < theElems.size(); idom++)
11212     {
11213       const TIDSortedElemSet& domain = theElems[idom];
11214       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11215       for (; elemItr != domain.end(); ++elemItr)
11216         {
11217           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11218           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11219           if (!aFace)
11220             continue;
11221           // MESSAGE("aFace=" << aFace->GetID());
11222           bool isQuad = aFace->IsQuadratic();
11223           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11224
11225           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11226
11227           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11228           while (nodeIt->more())
11229             {
11230               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11231               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11232               if (isMedium)
11233                 ln2.push_back(node);
11234               else
11235                 ln0.push_back(node);
11236
11237               const SMDS_MeshNode* clone = 0;
11238               if (!clonedNodes.count(node))
11239                 {
11240                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11241                   clonedNodes[node] = clone;
11242                 }
11243               else
11244                 clone = clonedNodes[node];
11245
11246               if (isMedium)
11247                 ln3.push_back(clone);
11248               else
11249                 ln1.push_back(clone);
11250
11251               const SMDS_MeshNode* inter = 0;
11252               if (isQuad && (!isMedium))
11253                 {
11254                   if (!intermediateNodes.count(node))
11255                     {
11256                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11257                       intermediateNodes[node] = inter;
11258                     }
11259                   else
11260                     inter = intermediateNodes[node];
11261                   ln4.push_back(inter);
11262                 }
11263             }
11264
11265           // --- extrude the face
11266
11267           vector<const SMDS_MeshNode*> ln;
11268           SMDS_MeshVolume* vol = 0;
11269           vtkIdType aType = aFace->GetVtkType();
11270           switch (aType)
11271           {
11272             case VTK_TRIANGLE:
11273               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11274               // MESSAGE("vol prism " << vol->GetID());
11275               ln.push_back(ln1[0]);
11276               ln.push_back(ln1[1]);
11277               ln.push_back(ln1[2]);
11278               break;
11279             case VTK_QUAD:
11280               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11281               // MESSAGE("vol hexa " << vol->GetID());
11282               ln.push_back(ln1[0]);
11283               ln.push_back(ln1[1]);
11284               ln.push_back(ln1[2]);
11285               ln.push_back(ln1[3]);
11286               break;
11287             case VTK_QUADRATIC_TRIANGLE:
11288               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11289                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11290               // MESSAGE("vol quad prism " << vol->GetID());
11291               ln.push_back(ln1[0]);
11292               ln.push_back(ln1[1]);
11293               ln.push_back(ln1[2]);
11294               ln.push_back(ln3[0]);
11295               ln.push_back(ln3[1]);
11296               ln.push_back(ln3[2]);
11297               break;
11298             case VTK_QUADRATIC_QUAD:
11299 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11300 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11301 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11302               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11303                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11304                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11305               // MESSAGE("vol quad hexa " << vol->GetID());
11306               ln.push_back(ln1[0]);
11307               ln.push_back(ln1[1]);
11308               ln.push_back(ln1[2]);
11309               ln.push_back(ln1[3]);
11310               ln.push_back(ln3[0]);
11311               ln.push_back(ln3[1]);
11312               ln.push_back(ln3[2]);
11313               ln.push_back(ln3[3]);
11314               break;
11315             case VTK_POLYGON:
11316               break;
11317             default:
11318               break;
11319           }
11320
11321           if (vol)
11322             {
11323               stringstream grpname;
11324               grpname << "jf_";
11325               grpname << idom;
11326               int idg;
11327               string namegrp = grpname.str();
11328               if (!mapOfJunctionGroups.count(namegrp))
11329                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11330               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11331               if (sgrp)
11332                 sgrp->Add(vol->GetID());
11333             }
11334
11335           // --- modify the face
11336
11337           aFace->ChangeNodes(&ln[0], ln.size());
11338         }
11339     }
11340   return true;
11341 }
11342
11343 /*!
11344  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11345  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11346  *  groups of faces to remove inside the object, (idem edges).
11347  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11348  */
11349 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11350                                       const TopoDS_Shape& theShape,
11351                                       SMESH_NodeSearcher* theNodeSearcher,
11352                                       const char* groupName,
11353                                       std::vector<double>&   nodesCoords,
11354                                       std::vector<std::vector<int> >& listOfListOfNodes)
11355 {
11356   MESSAGE("--------------------------------");
11357   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11358   MESSAGE("--------------------------------");
11359
11360   // --- zone of volumes to remove is given :
11361   //     1 either by a geom shape (one or more vertices) and a radius,
11362   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11363   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11364   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11365   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11366   //     defined by it's name.
11367
11368   SMESHDS_GroupBase* groupDS = 0;
11369   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11370   while ( groupIt->more() )
11371     {
11372       groupDS = 0;
11373       SMESH_Group * group = groupIt->next();
11374       if ( !group ) continue;
11375       groupDS = group->GetGroupDS();
11376       if ( !groupDS || groupDS->IsEmpty() ) continue;
11377       std::string grpName = group->GetName();
11378       //MESSAGE("grpName=" << grpName);
11379       if (grpName == groupName)
11380         break;
11381       else
11382         groupDS = 0;
11383     }
11384
11385   bool isNodeGroup = false;
11386   bool isNodeCoords = false;
11387   if (groupDS)
11388     {
11389       if (groupDS->GetType() != SMDSAbs_Node)
11390         return;
11391       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11392     }
11393
11394   if (nodesCoords.size() > 0)
11395     isNodeCoords = true; // a list o nodes given by their coordinates
11396   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11397
11398   // --- define groups to build
11399
11400   int idg; // --- group of SMDS volumes
11401   string grpvName = groupName;
11402   grpvName += "_vol";
11403   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11404   if (!grp)
11405     {
11406       MESSAGE("group not created " << grpvName);
11407       return;
11408     }
11409   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11410
11411   int idgs; // --- group of SMDS faces on the skin
11412   string grpsName = groupName;
11413   grpsName += "_skin";
11414   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11415   if (!grps)
11416     {
11417       MESSAGE("group not created " << grpsName);
11418       return;
11419     }
11420   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11421
11422   int idgi; // --- group of SMDS faces internal (several shapes)
11423   string grpiName = groupName;
11424   grpiName += "_internalFaces";
11425   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11426   if (!grpi)
11427     {
11428       MESSAGE("group not created " << grpiName);
11429       return;
11430     }
11431   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11432
11433   int idgei; // --- group of SMDS faces internal (several shapes)
11434   string grpeiName = groupName;
11435   grpeiName += "_internalEdges";
11436   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11437   if (!grpei)
11438     {
11439       MESSAGE("group not created " << grpeiName);
11440       return;
11441     }
11442   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11443
11444   // --- build downward connectivity
11445
11446   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11447   meshDS->BuildDownWardConnectivity(true);
11448   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11449
11450   // --- set of volumes detected inside
11451
11452   std::set<int> setOfInsideVol;
11453   std::set<int> setOfVolToCheck;
11454
11455   std::vector<gp_Pnt> gpnts;
11456   gpnts.clear();
11457
11458   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11459     {
11460       MESSAGE("group of nodes provided");
11461       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11462       while ( elemIt->more() )
11463         {
11464           const SMDS_MeshElement* elem = elemIt->next();
11465           if (!elem)
11466             continue;
11467           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11468           if (!node)
11469             continue;
11470           SMDS_MeshElement* vol = 0;
11471           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11472           while (volItr->more())
11473             {
11474               vol = (SMDS_MeshElement*)volItr->next();
11475               setOfInsideVol.insert(vol->getVtkId());
11476               sgrp->Add(vol->GetID());
11477             }
11478         }
11479     }
11480   else if (isNodeCoords)
11481     {
11482       MESSAGE("list of nodes coordinates provided");
11483       int i = 0;
11484       int k = 0;
11485       while (i < nodesCoords.size()-2)
11486         {
11487           double x = nodesCoords[i++];
11488           double y = nodesCoords[i++];
11489           double z = nodesCoords[i++];
11490           gp_Pnt p = gp_Pnt(x, y ,z);
11491           gpnts.push_back(p);
11492           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11493           k++;
11494         }
11495     }
11496   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11497     {
11498       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11499       TopTools_IndexedMapOfShape vertexMap;
11500       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11501       gp_Pnt p = gp_Pnt(0,0,0);
11502       if (vertexMap.Extent() < 1)
11503         return;
11504
11505       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11506         {
11507           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11508           p = BRep_Tool::Pnt(vertex);
11509           gpnts.push_back(p);
11510           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11511         }
11512     }
11513
11514   if (gpnts.size() > 0)
11515     {
11516       int nodeId = 0;
11517       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11518       if (startNode)
11519         nodeId = startNode->GetID();
11520       MESSAGE("nodeId " << nodeId);
11521
11522       double radius2 = radius*radius;
11523       MESSAGE("radius2 " << radius2);
11524
11525       // --- volumes on start node
11526
11527       setOfVolToCheck.clear();
11528       SMDS_MeshElement* startVol = 0;
11529       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11530       while (volItr->more())
11531         {
11532           startVol = (SMDS_MeshElement*)volItr->next();
11533           setOfVolToCheck.insert(startVol->getVtkId());
11534         }
11535       if (setOfVolToCheck.empty())
11536         {
11537           MESSAGE("No volumes found");
11538           return;
11539         }
11540
11541       // --- starting with central volumes then their neighbors, check if they are inside
11542       //     or outside the domain, until no more new neighbor volume is inside.
11543       //     Fill the group of inside volumes
11544
11545       std::map<int, double> mapOfNodeDistance2;
11546       mapOfNodeDistance2.clear();
11547       std::set<int> setOfOutsideVol;
11548       while (!setOfVolToCheck.empty())
11549         {
11550           std::set<int>::iterator it = setOfVolToCheck.begin();
11551           int vtkId = *it;
11552           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11553           bool volInside = false;
11554           vtkIdType npts = 0;
11555           vtkIdType* pts = 0;
11556           grid->GetCellPoints(vtkId, npts, pts);
11557           for (int i=0; i<npts; i++)
11558             {
11559               double distance2 = 0;
11560               if (mapOfNodeDistance2.count(pts[i]))
11561                 {
11562                   distance2 = mapOfNodeDistance2[pts[i]];
11563                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11564                 }
11565               else
11566                 {
11567                   double *coords = grid->GetPoint(pts[i]);
11568                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11569                   distance2 = 1.E40;
11570                   for (int j=0; j<gpnts.size(); j++)
11571                     {
11572                       double d2 = aPoint.SquareDistance(gpnts[j]);
11573                       if (d2 < distance2)
11574                         {
11575                           distance2 = d2;
11576                           if (distance2 < radius2)
11577                             break;
11578                         }
11579                     }
11580                   mapOfNodeDistance2[pts[i]] = distance2;
11581                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11582                 }
11583               if (distance2 < radius2)
11584                 {
11585                   volInside = true; // one or more nodes inside the domain
11586                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11587                   break;
11588                 }
11589             }
11590           if (volInside)
11591             {
11592               setOfInsideVol.insert(vtkId);
11593               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11594               int neighborsVtkIds[NBMAXNEIGHBORS];
11595               int downIds[NBMAXNEIGHBORS];
11596               unsigned char downTypes[NBMAXNEIGHBORS];
11597               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11598               for (int n = 0; n < nbNeighbors; n++)
11599                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11600                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11601             }
11602           else
11603             {
11604               setOfOutsideVol.insert(vtkId);
11605               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11606             }
11607           setOfVolToCheck.erase(vtkId);
11608         }
11609     }
11610
11611   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11612   //     If yes, add the volume to the inside set
11613
11614   bool addedInside = true;
11615   std::set<int> setOfVolToReCheck;
11616   while (addedInside)
11617     {
11618       MESSAGE(" --------------------------- re check");
11619       addedInside = false;
11620       std::set<int>::iterator itv = setOfInsideVol.begin();
11621       for (; itv != setOfInsideVol.end(); ++itv)
11622         {
11623           int vtkId = *itv;
11624           int neighborsVtkIds[NBMAXNEIGHBORS];
11625           int downIds[NBMAXNEIGHBORS];
11626           unsigned char downTypes[NBMAXNEIGHBORS];
11627           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11628           for (int n = 0; n < nbNeighbors; n++)
11629             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11630               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11631         }
11632       setOfVolToCheck = setOfVolToReCheck;
11633       setOfVolToReCheck.clear();
11634       while  (!setOfVolToCheck.empty())
11635         {
11636           std::set<int>::iterator it = setOfVolToCheck.begin();
11637           int vtkId = *it;
11638           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11639             {
11640               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11641               int countInside = 0;
11642               int neighborsVtkIds[NBMAXNEIGHBORS];
11643               int downIds[NBMAXNEIGHBORS];
11644               unsigned char downTypes[NBMAXNEIGHBORS];
11645               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11646               for (int n = 0; n < nbNeighbors; n++)
11647                 if (setOfInsideVol.count(neighborsVtkIds[n]))
11648                   countInside++;
11649               MESSAGE("countInside " << countInside);
11650               if (countInside > 1)
11651                 {
11652                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11653                   setOfInsideVol.insert(vtkId);
11654                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11655                   addedInside = true;
11656                 }
11657               else
11658                 setOfVolToReCheck.insert(vtkId);
11659             }
11660           setOfVolToCheck.erase(vtkId);
11661         }
11662     }
11663
11664   // --- map of Downward faces at the boundary, inside the global volume
11665   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11666   //     fill group of SMDS faces inside the volume (when several volume shapes)
11667   //     fill group of SMDS faces on the skin of the global volume (if skin)
11668
11669   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11670   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
11671   std::set<int>::iterator it = setOfInsideVol.begin();
11672   for (; it != setOfInsideVol.end(); ++it)
11673     {
11674       int vtkId = *it;
11675       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11676       int neighborsVtkIds[NBMAXNEIGHBORS];
11677       int downIds[NBMAXNEIGHBORS];
11678       unsigned char downTypes[NBMAXNEIGHBORS];
11679       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11680       for (int n = 0; n < nbNeighbors; n++)
11681         {
11682           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11683           if (neighborDim == 3)
11684             {
11685               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11686                 {
11687                   DownIdType face(downIds[n], downTypes[n]);
11688                   boundaryFaces[face] = vtkId;
11689                 }
11690               // if the face between to volumes is in the mesh, get it (internal face between shapes)
11691               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11692               if (vtkFaceId >= 0)
11693                 {
11694                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11695                   // find also the smds edges on this face
11696                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11697                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11698                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11699                   for (int i = 0; i < nbEdges; i++)
11700                     {
11701                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11702                       if (vtkEdgeId >= 0)
11703                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11704                     }
11705                 }
11706             }
11707           else if (neighborDim == 2) // skin of the volume
11708             {
11709               DownIdType face(downIds[n], downTypes[n]);
11710               skinFaces[face] = vtkId;
11711               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11712               if (vtkFaceId >= 0)
11713                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11714             }
11715         }
11716     }
11717
11718   // --- identify the edges constituting the wire of each subshape on the skin
11719   //     define polylines with the nodes of edges, equivalent to wires
11720   //     project polylines on subshapes, and partition, to get geom faces
11721
11722   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11723   std::set<int> emptySet;
11724   emptySet.clear();
11725   std::set<int> shapeIds;
11726
11727   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11728   while (itelem->more())
11729     {
11730       const SMDS_MeshElement *elem = itelem->next();
11731       int shapeId = elem->getshapeId();
11732       int vtkId = elem->getVtkId();
11733       if (!shapeIdToVtkIdSet.count(shapeId))
11734         {
11735           shapeIdToVtkIdSet[shapeId] = emptySet;
11736           shapeIds.insert(shapeId);
11737         }
11738       shapeIdToVtkIdSet[shapeId].insert(vtkId);
11739     }
11740
11741   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11742   std::set<DownIdType, DownIdCompare> emptyEdges;
11743   emptyEdges.clear();
11744
11745   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
11746   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11747     {
11748       int shapeId = itShape->first;
11749       MESSAGE(" --- Shape ID --- "<< shapeId);
11750       shapeIdToEdges[shapeId] = emptyEdges;
11751
11752       std::vector<int> nodesEdges;
11753
11754       std::set<int>::iterator its = itShape->second.begin();
11755       for (; its != itShape->second.end(); ++its)
11756         {
11757           int vtkId = *its;
11758           MESSAGE("     " << vtkId);
11759           int neighborsVtkIds[NBMAXNEIGHBORS];
11760           int downIds[NBMAXNEIGHBORS];
11761           unsigned char downTypes[NBMAXNEIGHBORS];
11762           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11763           for (int n = 0; n < nbNeighbors; n++)
11764             {
11765               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11766                 continue;
11767               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11768               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11769               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11770                 {
11771                   DownIdType edge(downIds[n], downTypes[n]);
11772                   if (!shapeIdToEdges[shapeId].count(edge))
11773                     {
11774                       shapeIdToEdges[shapeId].insert(edge);
11775                       int vtkNodeId[3];
11776                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11777                       nodesEdges.push_back(vtkNodeId[0]);
11778                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11779                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11780                     }
11781                 }
11782             }
11783         }
11784
11785       std::list<int> order;
11786       order.clear();
11787       if (nodesEdges.size() > 0)
11788         {
11789           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
11790           nodesEdges[0] = -1;
11791           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
11792           nodesEdges[1] = -1; // do not reuse this edge
11793           bool found = true;
11794           while (found)
11795             {
11796               int nodeTofind = order.back(); // try first to push back
11797               int i = 0;
11798               for (i = 0; i<nodesEdges.size(); i++)
11799                 if (nodesEdges[i] == nodeTofind)
11800                   break;
11801               if (i == nodesEdges.size())
11802                 found = false; // no follower found on back
11803               else
11804                 {
11805                   if (i%2) // odd ==> use the previous one
11806                     if (nodesEdges[i-1] < 0)
11807                       found = false;
11808                     else
11809                       {
11810                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
11811                         nodesEdges[i-1] = -1;
11812                       }
11813                   else // even ==> use the next one
11814                     if (nodesEdges[i+1] < 0)
11815                       found = false;
11816                     else
11817                       {
11818                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
11819                         nodesEdges[i+1] = -1;
11820                       }
11821                 }
11822               if (found)
11823                 continue;
11824               // try to push front
11825               found = true;
11826               nodeTofind = order.front(); // try to push front
11827               for (i = 0; i<nodesEdges.size(); i++)
11828                 if (nodesEdges[i] == nodeTofind)
11829                   break;
11830               if (i == nodesEdges.size())
11831                 {
11832                   found = false; // no predecessor found on front
11833                   continue;
11834                 }
11835               if (i%2) // odd ==> use the previous one
11836                 if (nodesEdges[i-1] < 0)
11837                   found = false;
11838                 else
11839                   {
11840                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
11841                     nodesEdges[i-1] = -1;
11842                   }
11843               else // even ==> use the next one
11844                 if (nodesEdges[i+1] < 0)
11845                   found = false;
11846                 else
11847                   {
11848                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
11849                     nodesEdges[i+1] = -1;
11850                   }
11851             }
11852         }
11853
11854
11855       std::vector<int> nodes;
11856       nodes.push_back(shapeId);
11857       std::list<int>::iterator itl = order.begin();
11858       for (; itl != order.end(); itl++)
11859         {
11860           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11861           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
11862         }
11863       listOfListOfNodes.push_back(nodes);
11864     }
11865
11866   //     partition geom faces with blocFissure
11867   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11868   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11869
11870   return;
11871 }
11872
11873
11874 //================================================================================
11875 /*!
11876  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11877  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11878  * \return TRUE if operation has been completed successfully, FALSE otherwise
11879  */
11880 //================================================================================
11881
11882 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11883 {
11884   // iterates on volume elements and detect all free faces on them
11885   SMESHDS_Mesh* aMesh = GetMeshDS();
11886   if (!aMesh)
11887     return false;
11888   //bool res = false;
11889   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11890   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11891   while(vIt->more())
11892   {
11893     const SMDS_MeshVolume* volume = vIt->next();
11894     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11895     vTool.SetExternalNormal();
11896     //const bool isPoly = volume->IsPoly();
11897     const int iQuad = volume->IsQuadratic();
11898     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11899     {
11900       if (!vTool.IsFreeFace(iface))
11901         continue;
11902       nbFree++;
11903       vector<const SMDS_MeshNode *> nodes;
11904       int nbFaceNodes = vTool.NbFaceNodes(iface);
11905       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11906       int inode = 0;
11907       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11908         nodes.push_back(faceNodes[inode]);
11909       if (iQuad) { // add medium nodes
11910         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11911           nodes.push_back(faceNodes[inode]);
11912         if ( nbFaceNodes == 9 ) // bi-quadratic quad
11913           nodes.push_back(faceNodes[8]);
11914       }
11915       // add new face based on volume nodes
11916       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11917         nbExisted++;
11918         continue; // face already exsist
11919       }
11920       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11921       nbCreated++;
11922     }
11923   }
11924   return ( nbFree==(nbExisted+nbCreated) );
11925 }
11926
11927 namespace
11928 {
11929   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11930   {
11931     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11932       return n;
11933     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11934   }
11935 }
11936 //================================================================================
11937 /*!
11938  * \brief Creates missing boundary elements
11939  *  \param elements - elements whose boundary is to be checked
11940  *  \param dimension - defines type of boundary elements to create
11941  *  \param group - a group to store created boundary elements in
11942  *  \param targetMesh - a mesh to store created boundary elements in
11943  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11944  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
11945  *                                boundary elements will be copied into the targetMesh
11946  *  \param toAddExistingBondary - if true, not only new but also pre-existing
11947  *                                boundary elements will be added into the new group
11948  *  \param aroundElements - if true, elements will be created on boundary of given
11949  *                          elements else, on boundary of the whole mesh.
11950  * \return nb of added boundary elements
11951  */
11952 //================================================================================
11953
11954 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11955                                        Bnd_Dimension           dimension,
11956                                        SMESH_Group*            group/*=0*/,
11957                                        SMESH_Mesh*             targetMesh/*=0*/,
11958                                        bool                    toCopyElements/*=false*/,
11959                                        bool                    toCopyExistingBoundary/*=false*/,
11960                                        bool                    toAddExistingBondary/*= false*/,
11961                                        bool                    aroundElements/*= false*/)
11962 {
11963   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11964   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11965   // hope that all elements are of the same type, do not check them all
11966   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11967     throw SALOME_Exception(LOCALIZED("wrong element type"));
11968
11969   if ( !targetMesh )
11970     toCopyElements = toCopyExistingBoundary = false;
11971
11972   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11973   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11974   int nbAddedBnd = 0;
11975
11976   // editor adding present bnd elements and optionally holding elements to add to the group
11977   SMESH_MeshEditor* presentEditor;
11978   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11979   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11980
11981   SMESH_MesherHelper helper( *myMesh );
11982   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11983   SMDS_VolumeTool vTool;
11984   TIDSortedElemSet avoidSet;
11985   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11986   int inode;
11987
11988   typedef vector<const SMDS_MeshNode*> TConnectivity;
11989
11990   SMDS_ElemIteratorPtr eIt;
11991   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11992   else                  eIt = elemSetIterator( elements );
11993
11994   while (eIt->more())
11995   {
11996     const SMDS_MeshElement* elem = eIt->next();
11997     const int              iQuad = elem->IsQuadratic();
11998
11999     // ------------------------------------------------------------------------------------
12000     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12001     // ------------------------------------------------------------------------------------
12002     vector<const SMDS_MeshElement*> presentBndElems;
12003     vector<TConnectivity>           missingBndElems;
12004     TConnectivity nodes, elemNodes;
12005     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12006     {
12007       vTool.SetExternalNormal();
12008       const SMDS_MeshElement* otherVol = 0;
12009       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12010       {
12011         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12012              ( !aroundElements || elements.count( otherVol )))
12013           continue;
12014         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12015         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12016         if ( missType == SMDSAbs_Edge ) // boundary edges
12017         {
12018           nodes.resize( 2+iQuad );
12019           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12020           {
12021             for ( int j = 0; j < nodes.size(); ++j )
12022               nodes[j] =nn[i+j];
12023             if ( const SMDS_MeshElement* edge =
12024                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12025               presentBndElems.push_back( edge );
12026             else
12027               missingBndElems.push_back( nodes );
12028           }
12029         }
12030         else // boundary face
12031         {
12032           nodes.clear();
12033           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12034             nodes.push_back( nn[inode] ); // add corner nodes
12035           if (iQuad)
12036             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12037               nodes.push_back( nn[inode] ); // add medium nodes
12038           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12039           if ( iCenter > 0 )
12040             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12041
12042           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12043                                                                SMDSAbs_Face, /*noMedium=*/false ))
12044             presentBndElems.push_back( f );
12045           else
12046             missingBndElems.push_back( nodes );
12047
12048           if ( targetMesh != myMesh )
12049           {
12050             // add 1D elements on face boundary to be added to a new mesh
12051             const SMDS_MeshElement* edge;
12052             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12053             {
12054               if ( iQuad )
12055                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12056               else
12057                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12058               if ( edge && avoidSet.insert( edge ).second )
12059                 presentBndElems.push_back( edge );
12060             }
12061           }
12062         }
12063       }
12064     }
12065     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12066     {
12067       avoidSet.clear(), avoidSet.insert( elem );
12068       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12069                         SMDS_MeshElement::iterator() );
12070       elemNodes.push_back( elemNodes[0] );
12071       nodes.resize( 2 + iQuad );
12072       const int nbLinks = elem->NbCornerNodes();
12073       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12074       {
12075         nodes[0] = elemNodes[iN];
12076         nodes[1] = elemNodes[iN+1+iQuad];
12077         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12078           continue; // not free link
12079
12080         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12081         if ( const SMDS_MeshElement* edge =
12082              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12083           presentBndElems.push_back( edge );
12084         else
12085           missingBndElems.push_back( nodes );
12086       }
12087     }
12088
12089     // ---------------------------------
12090     // 2. Add missing boundary elements
12091     // ---------------------------------
12092     if ( targetMesh != myMesh )
12093       // instead of making a map of nodes in this mesh and targetMesh,
12094       // we create nodes with same IDs.
12095       for ( int i = 0; i < missingBndElems.size(); ++i )
12096       {
12097         TConnectivity& srcNodes = missingBndElems[i];
12098         TConnectivity  nodes( srcNodes.size() );
12099         for ( inode = 0; inode < nodes.size(); ++inode )
12100           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12101         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12102                                                                    missType,
12103                                                                    /*noMedium=*/false))
12104           continue;
12105         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12106         ++nbAddedBnd;
12107       }
12108     else
12109       for ( int i = 0; i < missingBndElems.size(); ++i )
12110       {
12111         TConnectivity& nodes = missingBndElems[i];
12112         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12113                                                                    missType,
12114                                                                    /*noMedium=*/false))
12115           continue;
12116         SMDS_MeshElement* elem =
12117           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12118         ++nbAddedBnd;
12119
12120         // try to set a new element to a shape
12121         if ( myMesh->HasShapeToMesh() )
12122         {
12123           bool ok = true;
12124           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12125           const int nbN = nodes.size() / (iQuad+1 );
12126           for ( inode = 0; inode < nbN && ok; ++inode )
12127           {
12128             pair<int, TopAbs_ShapeEnum> i_stype =
12129               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12130             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12131               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12132           }
12133           if ( ok && mediumShapes.size() > 1 )
12134           {
12135             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12136             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12137             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12138             {
12139               if (( ok = ( stype_i->first != stype_i_0.first )))
12140                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12141                                         aMesh->IndexToShape( stype_i_0.second ));
12142             }
12143           }
12144           if ( ok && mediumShapes.begin()->first == missShapeType )
12145             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12146         }
12147       }
12148
12149     // ----------------------------------
12150     // 3. Copy present boundary elements
12151     // ----------------------------------
12152     if ( toCopyExistingBoundary )
12153       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12154       {
12155         const SMDS_MeshElement* e = presentBndElems[i];
12156         TConnectivity nodes( e->NbNodes() );
12157         for ( inode = 0; inode < nodes.size(); ++inode )
12158           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12159         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12160       }
12161     else // store present elements to add them to a group
12162       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12163       {
12164         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12165       }
12166
12167   } // loop on given elements
12168
12169   // ---------------------------------------------
12170   // 4. Fill group with boundary elements
12171   // ---------------------------------------------
12172   if ( group )
12173   {
12174     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12175       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12176         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12177   }
12178   tgtEditor.myLastCreatedElems.Clear();
12179   tgtEditor2.myLastCreatedElems.Clear();
12180
12181   // -----------------------
12182   // 5. Copy given elements
12183   // -----------------------
12184   if ( toCopyElements && targetMesh != myMesh )
12185   {
12186     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12187     else                  eIt = elemSetIterator( elements );
12188     while (eIt->more())
12189     {
12190       const SMDS_MeshElement* elem = eIt->next();
12191       TConnectivity nodes( elem->NbNodes() );
12192       for ( inode = 0; inode < nodes.size(); ++inode )
12193         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12194       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12195
12196       tgtEditor.myLastCreatedElems.Clear();
12197     }
12198   }
12199   return nbAddedBnd;
12200 }