Salome HOME
8ae8331ab9fa9a693e4d1fe004eba648a726acd6
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include <Basics_OCCTVersion.hxx>
50
51 #include "utilities.h"
52
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
57 #include <ElCLib.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
73 #include <TopExp.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
78 #include <TopoDS.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
81 #include <gp.hxx>
82 #include <gp_Ax1.hxx>
83 #include <gp_Dir.hxx>
84 #include <gp_Lin.hxx>
85 #include <gp_Pln.hxx>
86 #include <gp_Trsf.hxx>
87 #include <gp_Vec.hxx>
88 #include <gp_XY.hxx>
89 #include <gp_XYZ.hxx>
90
91 #include <cmath>
92
93 #include <map>
94 #include <set>
95 #include <numeric>
96 #include <limits>
97 #include <algorithm>
98 #include <sstream>
99
100 #include <boost/tuple/tuple.hpp>
101
102 #include <Standard_Failure.hxx>
103 #include <Standard_ErrorHandler.hxx>
104
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106
107 using namespace std;
108 using namespace SMESH::Controls;
109
110 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> >    TElemOfNodeListMap;
111 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
112
113 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
114
115 //=======================================================================
116 //function : SMESH_MeshEditor
117 //purpose  :
118 //=======================================================================
119
120 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
121   :myMesh( theMesh ) // theMesh may be NULL
122 {
123 }
124
125 //================================================================================
126 /*!
127  * \brief Clears myLastCreatedNodes and myLastCreatedElems
128  */
129 //================================================================================
130
131 void SMESH_MeshEditor::CrearLastCreated()
132 {
133   myLastCreatedNodes.Clear();
134   myLastCreatedElems.Clear();
135 }
136
137
138 //=======================================================================
139 /*!
140  * \brief Add element
141  */
142 //=======================================================================
143
144 SMDS_MeshElement*
145 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
146                              const SMDSAbs_ElementType            type,
147                              const bool                           isPoly,
148                              const int                            ID,
149                              const double                         ballDiameter)
150 {
151   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
152   SMDS_MeshElement* e = 0;
153   int nbnode = node.size();
154   SMESHDS_Mesh* mesh = GetMeshDS();
155   switch ( type ) {
156   case SMDSAbs_Face:
157     if ( !isPoly ) {
158       if      (nbnode == 3) {
159         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
160         else           e = mesh->AddFace      (node[0], node[1], node[2] );
161       }
162       else if (nbnode == 4) {
163         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
164         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
165       }
166       else if (nbnode == 6) {
167         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
168                                                node[4], node[5], ID);
169         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
170                                                node[4], node[5] );
171       }
172       else if (nbnode == 8) {
173         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
174                                                node[4], node[5], node[6], node[7], ID);
175         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
176                                                node[4], node[5], node[6], node[7] );
177       }
178       else if (nbnode == 9) {
179         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
180                                                node[4], node[5], node[6], node[7], node[8], ID);
181         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
182                                                node[4], node[5], node[6], node[7], node[8] );
183       }
184     } else {
185       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
186       else           e = mesh->AddPolygonalFace      (node    );
187     }
188     break;
189
190   case SMDSAbs_Volume:
191     if ( !isPoly ) {
192       if      (nbnode == 4) {
193         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
194         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
195       }
196       else if (nbnode == 5) {
197         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
198                                                  node[4], ID);
199         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
200                                                  node[4] );
201       }
202       else if (nbnode == 6) {
203         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
204                                                  node[4], node[5], ID);
205         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
206                                                  node[4], node[5] );
207       }
208       else if (nbnode == 8) {
209         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
210                                                  node[4], node[5], node[6], node[7], ID);
211         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], node[6], node[7] );
213       }
214       else if (nbnode == 10) {
215         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
216                                                  node[4], node[5], node[6], node[7],
217                                                  node[8], node[9], ID);
218         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
219                                                  node[4], node[5], node[6], node[7],
220                                                  node[8], node[9] );
221       }
222       else if (nbnode == 12) {
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], node[10], node[11], 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], node[10], node[11] );
229       }
230       else if (nbnode == 13) {
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],
234                                                  node[12],ID);
235         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
236                                                  node[4], node[5], node[6], node[7],
237                                                  node[8], node[9], node[10],node[11],
238                                                  node[12] );
239       }
240       else if (nbnode == 15) {
241         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
242                                                  node[4], node[5], node[6], node[7],
243                                                  node[8], node[9], node[10],node[11],
244                                                  node[12],node[13],node[14],ID);
245         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
246                                                  node[4], node[5], node[6], node[7],
247                                                  node[8], node[9], node[10],node[11],
248                                                  node[12],node[13],node[14] );
249       }
250       else if (nbnode == 20) {
251         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252                                                  node[4], node[5], node[6], node[7],
253                                                  node[8], node[9], node[10],node[11],
254                                                  node[12],node[13],node[14],node[15],
255                                                  node[16],node[17],node[18],node[19],ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
257                                                  node[4], node[5], node[6], node[7],
258                                                  node[8], node[9], node[10],node[11],
259                                                  node[12],node[13],node[14],node[15],
260                                                  node[16],node[17],node[18],node[19] );
261       }
262       else if (nbnode == 27) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], node[6], node[7],
265                                                  node[8], node[9], node[10],node[11],
266                                                  node[12],node[13],node[14],node[15],
267                                                  node[16],node[17],node[18],node[19],
268                                                  node[20],node[21],node[22],node[23],
269                                                  node[24],node[25],node[26], ID);
270         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
271                                                  node[4], node[5], node[6], node[7],
272                                                  node[8], node[9], node[10],node[11],
273                                                  node[12],node[13],node[14],node[15],
274                                                  node[16],node[17],node[18],node[19],
275                                                  node[20],node[21],node[22],node[23],
276                                                  node[24],node[25],node[26] );
277       }
278     }
279     break;
280
281   case SMDSAbs_Edge:
282     if ( nbnode == 2 ) {
283       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
284       else           e = mesh->AddEdge      (node[0], node[1] );
285     }
286     else if ( nbnode == 3 ) {
287       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
288       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
289     }
290     break;
291
292   case SMDSAbs_0DElement:
293     if ( nbnode == 1 ) {
294       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
295       else           e = mesh->Add0DElement      (node[0] );
296     }
297     break;
298
299   case SMDSAbs_Node:
300     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
301     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
302     break;
303
304   case SMDSAbs_Ball:
305     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
306     else           e = mesh->AddBall      (node[0], ballDiameter);
307     break;
308
309   default:;
310   }
311   if ( e ) myLastCreatedElems.Append( e );
312   return e;
313 }
314
315 //=======================================================================
316 /*!
317  * \brief Add element
318  */
319 //=======================================================================
320
321 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
322                                                const SMDSAbs_ElementType type,
323                                                const bool                isPoly,
324                                                const int                 ID)
325 {
326   vector<const SMDS_MeshNode*> nodes;
327   nodes.reserve( nodeIDs.size() );
328   vector<int>::const_iterator id = nodeIDs.begin();
329   while ( id != nodeIDs.end() ) {
330     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
331       nodes.push_back( node );
332     else
333       return 0;
334   }
335   return AddElement( nodes, type, isPoly, ID );
336 }
337
338 //=======================================================================
339 //function : Remove
340 //purpose  : Remove a node or an element.
341 //           Modify a compute state of sub-meshes which become empty
342 //=======================================================================
343
344 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
345                               const bool         isNodes )
346 {
347   myLastCreatedElems.Clear();
348   myLastCreatedNodes.Clear();
349
350   SMESHDS_Mesh* aMesh = GetMeshDS();
351   set< SMESH_subMesh *> smmap;
352
353   int removed = 0;
354   list<int>::const_iterator it = theIDs.begin();
355   for ( ; it != theIDs.end(); it++ ) {
356     const SMDS_MeshElement * elem;
357     if ( isNodes )
358       elem = aMesh->FindNode( *it );
359     else
360       elem = aMesh->FindElement( *it );
361     if ( !elem )
362       continue;
363
364     // Notify VERTEX sub-meshes about modification
365     if ( isNodes ) {
366       const SMDS_MeshNode* node = cast2Node( elem );
367       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
368         if ( int aShapeID = node->getshapeId() )
369           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
370             smmap.insert( sm );
371     }
372     // Find sub-meshes to notify about modification
373     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
374     //     while ( nodeIt->more() ) {
375     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
376     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
377     //       if ( aPosition.get() ) {
378     //         if ( int aShapeID = aPosition->GetShapeId() ) {
379     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
380     //             smmap.insert( sm );
381     //         }
382     //       }
383     //     }
384
385     // Do remove
386     if ( isNodes )
387       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
388     else
389       aMesh->RemoveElement( elem );
390     removed++;
391   }
392
393   // Notify sub-meshes about modification
394   if ( !smmap.empty() ) {
395     set< SMESH_subMesh *>::iterator smIt;
396     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
397       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
398   }
399
400   //   // Check if the whole mesh becomes empty
401   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
402   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
403
404   return removed;
405 }
406
407 //================================================================================
408 /*!
409  * \brief Create 0D elements on all nodes of the given object except those
410  *        nodes on which a 0D element already exists.
411  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
412  *                    the all mesh is treated
413  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
414  */
415 //================================================================================
416
417 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
418                                                    TIDSortedElemSet&       all0DElems )
419 {
420   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
421   SMDS_ElemIteratorPtr elemIt;
422   if ( elements.empty() )
423     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
424   else
425     elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
426
427   while ( elemIt->more() )
428   {
429     const SMDS_MeshElement* e = elemIt->next();
430     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
431     while ( nodeIt->more() )
432     {
433       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
434       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
435       if ( it0D->more() )
436         all0DElems.insert( it0D->next() );
437       else {
438         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
439         all0DElems.insert( myLastCreatedElems.Last() );
440       }
441     }
442   }
443 }
444
445 //=======================================================================
446 //function : FindShape
447 //purpose  : Return an index of the shape theElem is on
448 //           or zero if a shape not found
449 //=======================================================================
450
451 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
452 {
453   myLastCreatedElems.Clear();
454   myLastCreatedNodes.Clear();
455
456   SMESHDS_Mesh * aMesh = GetMeshDS();
457   if ( aMesh->ShapeToMesh().IsNull() )
458     return 0;
459
460   int aShapeID = theElem->getshapeId();
461   if ( aShapeID < 1 )
462     return 0;
463
464   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
465     if ( sm->Contains( theElem ))
466       return aShapeID;
467
468   if ( theElem->GetType() == SMDSAbs_Node ) {
469     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
470   }
471   else {
472     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
473   }
474
475   TopoDS_Shape aShape; // the shape a node of theElem is on
476   if ( theElem->GetType() != SMDSAbs_Node )
477   {
478     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
479     while ( nodeIt->more() ) {
480       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
481       if ((aShapeID = node->getshapeId()) > 0) {
482         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
483           if ( sm->Contains( theElem ))
484             return aShapeID;
485           if ( aShape.IsNull() )
486             aShape = aMesh->IndexToShape( aShapeID );
487         }
488       }
489     }
490   }
491
492   // None of nodes is on a proper shape,
493   // find the shape among ancestors of aShape on which a node is
494   if ( !aShape.IsNull() ) {
495     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
496     for ( ; ancIt.More(); ancIt.Next() ) {
497       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
498       if ( sm && sm->Contains( theElem ))
499         return aMesh->ShapeToIndex( ancIt.Value() );
500     }
501   }
502   else
503   {
504     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
505     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
506     for ( ; id_sm != id2sm.end(); ++id_sm )
507       if ( id_sm->second->Contains( theElem ))
508         return id_sm->first;
509   }
510
511   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
512   return 0;
513 }
514
515 //=======================================================================
516 //function : IsMedium
517 //purpose  :
518 //=======================================================================
519
520 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
521                                 const SMDSAbs_ElementType typeToCheck)
522 {
523   bool isMedium = false;
524   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
525   while (it->more() && !isMedium ) {
526     const SMDS_MeshElement* elem = it->next();
527     isMedium = elem->IsMediumNode(node);
528   }
529   return isMedium;
530 }
531
532 //=======================================================================
533 //function : ShiftNodesQuadTria
534 //purpose  : auxilary
535 //           Shift nodes in the array corresponded to quadratic triangle
536 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
537 //=======================================================================
538 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
539 {
540   const SMDS_MeshNode* nd1 = aNodes[0];
541   aNodes[0] = aNodes[1];
542   aNodes[1] = aNodes[2];
543   aNodes[2] = nd1;
544   const SMDS_MeshNode* nd2 = aNodes[3];
545   aNodes[3] = aNodes[4];
546   aNodes[4] = aNodes[5];
547   aNodes[5] = nd2;
548 }
549
550 //=======================================================================
551 //function : edgeConnectivity
552 //purpose  : auxilary
553 //           return number of the edges connected with the theNode.
554 //           if theEdges has connections with the other type of the
555 //           elements, return -1
556 //=======================================================================
557 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
558 {
559   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
560   int nb=0;
561   while(elemIt->more()) {
562     elemIt->next();
563     nb++;
564   }
565   return nb;
566 }
567
568
569 //=======================================================================
570 //function : GetNodesFromTwoTria
571 //purpose  : auxilary
572 //           Shift nodes in the array corresponded to quadratic triangle
573 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
574 //=======================================================================
575 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
576                                 const SMDS_MeshElement * theTria2,
577                                 const SMDS_MeshNode* N1[],
578                                 const SMDS_MeshNode* N2[])
579 {
580   SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
581   int i=0;
582   while(i<6) {
583     N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
584     i++;
585   }
586   if(it->more()) return false;
587   it = theTria2->nodesIterator();
588   i=0;
589   while(i<6) {
590     N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
591     i++;
592   }
593   if(it->more()) return false;
594
595   int sames[3] = {-1,-1,-1};
596   int nbsames = 0;
597   int j;
598   for(i=0; i<3; i++) {
599     for(j=0; j<3; j++) {
600       if(N1[i]==N2[j]) {
601         sames[i] = j;
602         nbsames++;
603         break;
604       }
605     }
606   }
607   if(nbsames!=2) return false;
608   if(sames[0]>-1) {
609     ShiftNodesQuadTria(N1);
610     if(sames[1]>-1) {
611       ShiftNodesQuadTria(N1);
612     }
613   }
614   i = sames[0] + sames[1] + sames[2];
615   for(; i<2; i++) {
616     ShiftNodesQuadTria(N2);
617   }
618   // now we receive following N1 and N2 (using numeration as above image)
619   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
620   // i.e. first nodes from both arrays determ new diagonal
621   return true;
622 }
623
624 //=======================================================================
625 //function : InverseDiag
626 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
627 //           but having other common link.
628 //           Return False if args are improper
629 //=======================================================================
630
631 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
632                                     const SMDS_MeshElement * theTria2 )
633 {
634   MESSAGE("InverseDiag");
635   myLastCreatedElems.Clear();
636   myLastCreatedNodes.Clear();
637
638   if (!theTria1 || !theTria2)
639     return false;
640
641   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
642   if (!F1) return false;
643   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
644   if (!F2) return false;
645   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
646       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
647
648     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
649     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
650     //    |/ |                                         | \|
651     //  B +--+ 2                                     B +--+ 2
652
653     // put nodes in array and find out indices of the same ones
654     const SMDS_MeshNode* aNodes [6];
655     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
656     int i = 0;
657     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
658     while ( it->more() ) {
659       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
660
661       if ( i > 2 ) // theTria2
662         // find same node of theTria1
663         for ( int j = 0; j < 3; j++ )
664           if ( aNodes[ i ] == aNodes[ j ]) {
665             sameInd[ j ] = i;
666             sameInd[ i ] = j;
667             break;
668           }
669       // next
670       i++;
671       if ( i == 3 ) {
672         if ( it->more() )
673           return false; // theTria1 is not a triangle
674         it = theTria2->nodesIterator();
675       }
676       if ( i == 6 && it->more() )
677         return false; // theTria2 is not a triangle
678     }
679
680     // find indices of 1,2 and of A,B in theTria1
681     int iA = 0, iB = 0, i1 = 0, i2 = 0;
682     for ( i = 0; i < 6; i++ ) {
683       if ( sameInd [ i ] == 0 ) {
684         if ( i < 3 ) i1 = i;
685         else         i2 = i;
686       }
687       else if (i < 3) {
688         if ( iA ) iB = i;
689         else      iA = i;
690       }
691     }
692     // nodes 1 and 2 should not be the same
693     if ( aNodes[ i1 ] == aNodes[ i2 ] )
694       return false;
695
696     // theTria1: A->2
697     aNodes[ iA ] = aNodes[ i2 ];
698     // theTria2: B->1
699     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
700
701     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
702     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
703
704     return true;
705
706   } // end if(F1 && F2)
707
708   // check case of quadratic faces
709   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
710     return false;
711   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
712     return false;
713
714   //       5
715   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
716   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
717   //    |   / |
718   //  7 +  +  + 6
719   //    | /9  |
720   //    |/    |
721   //  4 +--+--+ 3
722   //       8
723
724   const SMDS_MeshNode* N1 [6];
725   const SMDS_MeshNode* N2 [6];
726   if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
727     return false;
728   // now we receive following N1 and N2 (using numeration as above image)
729   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
730   // i.e. first nodes from both arrays determ new diagonal
731
732   const SMDS_MeshNode* N1new [6];
733   const SMDS_MeshNode* N2new [6];
734   N1new[0] = N1[0];
735   N1new[1] = N2[0];
736   N1new[2] = N2[1];
737   N1new[3] = N1[4];
738   N1new[4] = N2[3];
739   N1new[5] = N1[5];
740   N2new[0] = N1[0];
741   N2new[1] = N1[1];
742   N2new[2] = N2[0];
743   N2new[3] = N1[3];
744   N2new[4] = N2[5];
745   N2new[5] = N1[4];
746   // replaces nodes in faces
747   GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
748   GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
749
750   return true;
751 }
752
753 //=======================================================================
754 //function : findTriangles
755 //purpose  : find triangles sharing theNode1-theNode2 link
756 //=======================================================================
757
758 static bool findTriangles(const SMDS_MeshNode *    theNode1,
759                           const SMDS_MeshNode *    theNode2,
760                           const SMDS_MeshElement*& theTria1,
761                           const SMDS_MeshElement*& theTria2)
762 {
763   if ( !theNode1 || !theNode2 ) return false;
764
765   theTria1 = theTria2 = 0;
766
767   set< const SMDS_MeshElement* > emap;
768   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
769   while (it->more()) {
770     const SMDS_MeshElement* elem = it->next();
771     if ( elem->NbNodes() == 3 )
772       emap.insert( elem );
773   }
774   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
775   while (it->more()) {
776     const SMDS_MeshElement* elem = it->next();
777     if ( emap.find( elem ) != emap.end() ) {
778       if ( theTria1 ) {
779         // theTria1 must be element with minimum ID
780         if( theTria1->GetID() < elem->GetID() ) {
781           theTria2 = elem;
782         }
783         else {
784           theTria2 = theTria1;
785           theTria1 = elem;
786         }
787         break;
788       }
789       else {
790         theTria1 = elem;
791       }
792     }
793   }
794   return ( theTria1 && theTria2 );
795 }
796
797 //=======================================================================
798 //function : InverseDiag
799 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
800 //           with ones built on the same 4 nodes but having other common link.
801 //           Return false if proper faces not found
802 //=======================================================================
803
804 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
805                                     const SMDS_MeshNode * theNode2)
806 {
807   myLastCreatedElems.Clear();
808   myLastCreatedNodes.Clear();
809
810   MESSAGE( "::InverseDiag()" );
811
812   const SMDS_MeshElement *tr1, *tr2;
813   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
814     return false;
815
816   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
817   if (!F1) return false;
818   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
819   if (!F2) return false;
820   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
821       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
822
823     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
824     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
825     //    |/ |                                    | \|
826     //  B +--+ 2                                B +--+ 2
827
828     // put nodes in array
829     // and find indices of 1,2 and of A in tr1 and of B in tr2
830     int i, iA1 = 0, i1 = 0;
831     const SMDS_MeshNode* aNodes1 [3];
832     SMDS_ElemIteratorPtr it;
833     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
834       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
835       if ( aNodes1[ i ] == theNode1 )
836         iA1 = i; // node A in tr1
837       else if ( aNodes1[ i ] != theNode2 )
838         i1 = i;  // node 1
839     }
840     int iB2 = 0, i2 = 0;
841     const SMDS_MeshNode* aNodes2 [3];
842     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
843       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
844       if ( aNodes2[ i ] == theNode2 )
845         iB2 = i; // node B in tr2
846       else if ( aNodes2[ i ] != theNode1 )
847         i2 = i;  // node 2
848     }
849
850     // nodes 1 and 2 should not be the same
851     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
852       return false;
853
854     // tr1: A->2
855     aNodes1[ iA1 ] = aNodes2[ i2 ];
856     // tr2: B->1
857     aNodes2[ iB2 ] = aNodes1[ i1 ];
858
859     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
860     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
861
862     return true;
863   }
864
865   // check case of quadratic faces
866   return InverseDiag(tr1,tr2);
867 }
868
869 //=======================================================================
870 //function : getQuadrangleNodes
871 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
872 //           fusion of triangles tr1 and tr2 having shared link on
873 //           theNode1 and theNode2
874 //=======================================================================
875
876 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
877                         const SMDS_MeshNode *    theNode1,
878                         const SMDS_MeshNode *    theNode2,
879                         const SMDS_MeshElement * tr1,
880                         const SMDS_MeshElement * tr2 )
881 {
882   if( tr1->NbNodes() != tr2->NbNodes() )
883     return false;
884   // find the 4-th node to insert into tr1
885   const SMDS_MeshNode* n4 = 0;
886   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
887   int i=0;
888   while ( !n4 && i<3 ) {
889     const SMDS_MeshNode * n = cast2Node( it->next() );
890     i++;
891     bool isDiag = ( n == theNode1 || n == theNode2 );
892     if ( !isDiag )
893       n4 = n;
894   }
895   // Make an array of nodes to be in a quadrangle
896   int iNode = 0, iFirstDiag = -1;
897   it = tr1->nodesIterator();
898   i=0;
899   while ( i<3 ) {
900     const SMDS_MeshNode * n = cast2Node( it->next() );
901     i++;
902     bool isDiag = ( n == theNode1 || n == theNode2 );
903     if ( isDiag ) {
904       if ( iFirstDiag < 0 )
905         iFirstDiag = iNode;
906       else if ( iNode - iFirstDiag == 1 )
907         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
908     }
909     else if ( n == n4 ) {
910       return false; // tr1 and tr2 should not have all the same nodes
911     }
912     theQuadNodes[ iNode++ ] = n;
913   }
914   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
915     theQuadNodes[ iNode ] = n4;
916
917   return true;
918 }
919
920 //=======================================================================
921 //function : DeleteDiag
922 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
923 //           with a quadrangle built on the same 4 nodes.
924 //           Return false if proper faces not found
925 //=======================================================================
926
927 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
928                                    const SMDS_MeshNode * theNode2)
929 {
930   myLastCreatedElems.Clear();
931   myLastCreatedNodes.Clear();
932
933   MESSAGE( "::DeleteDiag()" );
934
935   const SMDS_MeshElement *tr1, *tr2;
936   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
937     return false;
938
939   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
940   if (!F1) return false;
941   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
942   if (!F2) return false;
943   SMESHDS_Mesh * aMesh = GetMeshDS();
944
945   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
946       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
947
948     const SMDS_MeshNode* aNodes [ 4 ];
949     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
950       return false;
951
952     const SMDS_MeshElement* newElem = 0;
953     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
954     myLastCreatedElems.Append(newElem);
955     AddToSameGroups( newElem, tr1, aMesh );
956     int aShapeId = tr1->getshapeId();
957     if ( aShapeId )
958       {
959         aMesh->SetMeshElementOnShape( newElem, aShapeId );
960       }
961     aMesh->RemoveElement( tr1 );
962     aMesh->RemoveElement( tr2 );
963
964     return true;
965   }
966
967   // check case of quadratic faces
968   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
969     return false;
970   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
971     return false;
972
973   //       5
974   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
975   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
976   //    |   / |
977   //  7 +  +  + 6
978   //    | /9  |
979   //    |/    |
980   //  4 +--+--+ 3
981   //       8
982
983   const SMDS_MeshNode* N1 [6];
984   const SMDS_MeshNode* N2 [6];
985   if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
986     return false;
987   // now we receive following N1 and N2 (using numeration as above image)
988   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
989   // i.e. first nodes from both arrays determ new diagonal
990
991   const SMDS_MeshNode* aNodes[8];
992   aNodes[0] = N1[0];
993   aNodes[1] = N1[1];
994   aNodes[2] = N2[0];
995   aNodes[3] = N2[1];
996   aNodes[4] = N1[3];
997   aNodes[5] = N2[5];
998   aNodes[6] = N2[3];
999   aNodes[7] = N1[5];
1000
1001   const SMDS_MeshElement* newElem = 0;
1002   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1003                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1004   myLastCreatedElems.Append(newElem);
1005   AddToSameGroups( newElem, tr1, aMesh );
1006   int aShapeId = tr1->getshapeId();
1007   if ( aShapeId )
1008     {
1009       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1010     }
1011   aMesh->RemoveElement( tr1 );
1012   aMesh->RemoveElement( tr2 );
1013
1014   // remove middle node (9)
1015   GetMeshDS()->RemoveNode( N1[4] );
1016
1017   return true;
1018 }
1019
1020 //=======================================================================
1021 //function : Reorient
1022 //purpose  : Reverse theElement orientation
1023 //=======================================================================
1024
1025 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1026 {
1027   MESSAGE("Reorient");
1028   myLastCreatedElems.Clear();
1029   myLastCreatedNodes.Clear();
1030
1031   if (!theElem)
1032     return false;
1033   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1034   if ( !it || !it->more() )
1035     return false;
1036
1037   switch ( theElem->GetType() ) {
1038
1039   case SMDSAbs_Edge:
1040   case SMDSAbs_Face: {
1041     if(!theElem->IsQuadratic()) {
1042       int i = theElem->NbNodes();
1043       vector<const SMDS_MeshNode*> aNodes( i );
1044       while ( it->more() )
1045         aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1046       return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1047     }
1048     else {
1049       // quadratic elements
1050       if(theElem->GetType()==SMDSAbs_Edge) {
1051         vector<const SMDS_MeshNode*> aNodes(3);
1052         aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1053         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1054         aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1055         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1056       }
1057       else {
1058         int nbn = theElem->NbNodes();
1059         vector<const SMDS_MeshNode*> aNodes(nbn);
1060         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1061         int i=1;
1062         for(; i<nbn/2; i++) {
1063           aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1064         }
1065         for(i=0; i<nbn/2; i++) {
1066           aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1067         }
1068         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1069       }
1070     }
1071   }
1072   case SMDSAbs_Volume: {
1073     if (theElem->IsPoly()) {
1074       // TODO reorient vtk polyhedron
1075       MESSAGE("reorient vtk polyhedron ?");
1076       const SMDS_VtkVolume* aPolyedre =
1077         dynamic_cast<const SMDS_VtkVolume*>( theElem );
1078       if (!aPolyedre) {
1079         MESSAGE("Warning: bad volumic element");
1080         return false;
1081       }
1082
1083       int nbFaces = aPolyedre->NbFaces();
1084       vector<const SMDS_MeshNode *> poly_nodes;
1085       vector<int> quantities (nbFaces);
1086
1087       // reverse each face of the polyedre
1088       for (int iface = 1; iface <= nbFaces; iface++) {
1089         int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1090         quantities[iface - 1] = nbFaceNodes;
1091
1092         for (inode = nbFaceNodes; inode >= 1; inode--) {
1093           const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1094           poly_nodes.push_back(curNode);
1095         }
1096       }
1097
1098       return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1099
1100     }
1101     else {
1102       SMDS_VolumeTool vTool;
1103       if ( !vTool.Set( theElem ))
1104         return false;
1105       vTool.Inverse();
1106       MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1107       return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1108     }
1109   }
1110   default:;
1111   }
1112
1113   return false;
1114 }
1115
1116 //================================================================================
1117 /*!
1118  * \brief Reorient faces.
1119  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1120  * \param theDirection - desired direction of normal of \a theFace
1121  * \param theFace - one of \a theFaces that sould be oriented according to
1122  *        \a theDirection and whose orientation defines orientation of other faces
1123  * \return number of reoriented faces.
1124  */
1125 //================================================================================
1126
1127 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1128                                   const gp_Dir&            theDirection,
1129                                   const SMDS_MeshElement * theFace)
1130 {
1131   int nbReori = 0;
1132   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1133
1134   if ( theFaces.empty() )
1135   {
1136     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1137     while ( fIt->more() )
1138       theFaces.insert( theFaces.end(), fIt->next() );
1139   }
1140
1141   // orient theFace according to theDirection
1142   gp_XYZ normal;
1143   SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1144   if ( normal * theDirection.XYZ() < 0 )
1145     nbReori += Reorient( theFace );
1146
1147   // Orient other faces
1148
1149   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1150   TIDSortedElemSet avoidSet;
1151   set< SMESH_TLink > checkedLinks;
1152   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1153
1154   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1155     theFaces.erase( theFace );
1156   startFaces.insert( theFace );
1157
1158   int nodeInd1, nodeInd2;
1159   const SMDS_MeshElement*           otherFace;
1160   vector< const SMDS_MeshElement* > facesNearLink;
1161   vector< std::pair< int, int > >   nodeIndsOfFace;
1162
1163   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1164   while ( !startFaces.empty() )
1165   {
1166     startFace = startFaces.begin();
1167     theFace = *startFace;
1168     startFaces.erase( startFace );
1169     if ( !visitedFaces.insert( theFace ).second )
1170       continue;
1171
1172     avoidSet.clear();
1173     avoidSet.insert(theFace);
1174
1175     NLink link( theFace->GetNode( 0 ), 0 );
1176
1177     const int nbNodes = theFace->NbCornerNodes();
1178     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1179     {
1180       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1181       linkIt_isNew = checkedLinks.insert( link );
1182       if ( !linkIt_isNew.second )
1183       {
1184         // link has already been checked and won't be encountered more
1185         // if the group (theFaces) is manifold
1186         //checkedLinks.erase( linkIt_isNew.first );
1187       }
1188       else
1189       {
1190         facesNearLink.clear();
1191         nodeIndsOfFace.clear();
1192         while (( otherFace = FindFaceInSet( link.first, link.second,
1193                                             theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1194           if ( otherFace != theFace)
1195           {
1196             facesNearLink.push_back( otherFace );
1197             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1198             avoidSet.insert( otherFace );
1199           }
1200         if ( facesNearLink.size() > 1 )
1201         {
1202           // NON-MANIFOLD mesh shell !
1203           // select a face most co-directed with theFace,
1204           // other faces won't be visited this time
1205           gp_XYZ NF, NOF;
1206           SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1207           double proj, maxProj = -1;
1208           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1209             SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1210             if (( proj = Abs( NF * NOF )) > maxProj ) {
1211               maxProj = proj;
1212               otherFace = facesNearLink[i];
1213               nodeInd1  = nodeIndsOfFace[i].first;
1214               nodeInd2  = nodeIndsOfFace[i].second;
1215             }
1216           }
1217           // not to visit rejected faces
1218           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1219             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1220               visitedFaces.insert( facesNearLink[i] );
1221         }
1222         else if ( facesNearLink.size() == 1 )
1223         {
1224           otherFace = facesNearLink[0];
1225           nodeInd1  = nodeIndsOfFace.back().first;
1226           nodeInd2  = nodeIndsOfFace.back().second;
1227         }
1228         if ( otherFace && otherFace != theFace)
1229         {
1230           // link must be reverse in otherFace if orientation ot otherFace
1231           // is same as that of theFace
1232           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1233           {
1234             nbReori += Reorient( otherFace );
1235           }
1236           startFaces.insert( otherFace );
1237         }
1238       }
1239       std::swap( link.first, link.second ); // reverse the link
1240     }
1241   }
1242   return nbReori;
1243 }
1244
1245 //=======================================================================
1246 //function : getBadRate
1247 //purpose  :
1248 //=======================================================================
1249
1250 static double getBadRate (const SMDS_MeshElement*               theElem,
1251                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1252 {
1253   SMESH::Controls::TSequenceOfXYZ P;
1254   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1255     return 1e100;
1256   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1257   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1258 }
1259
1260 //=======================================================================
1261 //function : QuadToTri
1262 //purpose  : Cut quadrangles into triangles.
1263 //           theCrit is used to select a diagonal to cut
1264 //=======================================================================
1265
1266 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1267                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1268 {
1269   myLastCreatedElems.Clear();
1270   myLastCreatedNodes.Clear();
1271
1272   MESSAGE( "::QuadToTri()" );
1273
1274   if ( !theCrit.get() )
1275     return false;
1276
1277   SMESHDS_Mesh * aMesh = GetMeshDS();
1278
1279   Handle(Geom_Surface) surface;
1280   SMESH_MesherHelper   helper( *GetMesh() );
1281
1282   TIDSortedElemSet::iterator itElem;
1283   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1284     const SMDS_MeshElement* elem = *itElem;
1285     if ( !elem || elem->GetType() != SMDSAbs_Face )
1286       continue;
1287     if ( elem->NbCornerNodes() != 4 )
1288       continue;
1289
1290     // retrieve element nodes
1291     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1292
1293     // compare two sets of possible triangles
1294     double aBadRate1, aBadRate2; // to what extent a set is bad
1295     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1296     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1297     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1298
1299     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1300     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1301     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1302
1303     int aShapeId = FindShape( elem );
1304     const SMDS_MeshElement* newElem1 = 0;
1305     const SMDS_MeshElement* newElem2 = 0;
1306
1307     if( !elem->IsQuadratic() ) {
1308
1309       // split liner quadrangle
1310       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1311       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1312       if ( aBadRate1 <= aBadRate2 ) {
1313         // tr1 + tr2 is better
1314         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1315         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1316       }
1317       else {
1318         // tr3 + tr4 is better
1319         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1320         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1321       }
1322     }
1323     else {
1324
1325       // split quadratic quadrangle
1326
1327       // get surface elem is on
1328       if ( aShapeId != helper.GetSubShapeID() ) {
1329         surface.Nullify();
1330         TopoDS_Shape shape;
1331         if ( aShapeId > 0 )
1332           shape = aMesh->IndexToShape( aShapeId );
1333         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1334           TopoDS_Face face = TopoDS::Face( shape );
1335           surface = BRep_Tool::Surface( face );
1336           if ( !surface.IsNull() )
1337             helper.SetSubShape( shape );
1338         }
1339       }
1340       // find middle point for (0,1,2,3)
1341       // and create a node in this point;
1342       const SMDS_MeshNode* newN = 0;
1343       if ( aNodes.size() == 9 )
1344       {
1345         // SMDSEntity_BiQuad_Quadrangle
1346         newN = aNodes.back();
1347       }
1348       else
1349       {
1350         gp_XYZ p( 0,0,0 );
1351         if ( surface.IsNull() )
1352         {
1353           for ( int i = 0; i < 4; i++ )
1354             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1355           p /= 4;
1356         }
1357         else
1358         {
1359           const SMDS_MeshNode* inFaceNode = 0;
1360           if ( helper.GetNodeUVneedInFaceNode() )
1361             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1362               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1363                 inFaceNode = aNodes[ i ];
1364
1365           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1366           gp_XY uv( 0,0 );
1367           for ( int i = 0; i < 4; i++ )
1368             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1369           uv /= 4.;
1370           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1371         }
1372         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1373         myLastCreatedNodes.Append(newN);
1374       }
1375       // create a new element
1376       if ( aBadRate1 <= aBadRate2 ) {
1377         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1378                                   aNodes[6], aNodes[7], newN );
1379         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1380                                   newN,      aNodes[4], aNodes[5] );
1381       }
1382       else {
1383         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1384                                   aNodes[7], aNodes[4], newN );
1385         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1386                                   newN,      aNodes[5], aNodes[6] );
1387       }
1388     } // quadratic case
1389
1390     // care of a new element
1391
1392     myLastCreatedElems.Append(newElem1);
1393     myLastCreatedElems.Append(newElem2);
1394     AddToSameGroups( newElem1, elem, aMesh );
1395     AddToSameGroups( newElem2, elem, aMesh );
1396
1397     // put a new triangle on the same shape
1398     if ( aShapeId )
1399       {
1400         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1401         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1402       }
1403     aMesh->RemoveElement( elem );
1404   }
1405   return true;
1406 }
1407
1408 //=======================================================================
1409 //function : BestSplit
1410 //purpose  : Find better diagonal for cutting.
1411 //=======================================================================
1412
1413 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1414                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1415 {
1416   myLastCreatedElems.Clear();
1417   myLastCreatedNodes.Clear();
1418
1419   if (!theCrit.get())
1420     return -1;
1421
1422   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1423     return -1;
1424
1425   if( theQuad->NbNodes()==4 ||
1426       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1427
1428     // retrieve element nodes
1429     const SMDS_MeshNode* aNodes [4];
1430     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1431     int i = 0;
1432     //while (itN->more())
1433     while (i<4) {
1434       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1435     }
1436     // compare two sets of possible triangles
1437     double aBadRate1, aBadRate2; // to what extent a set is bad
1438     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1439     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1440     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1441
1442     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1443     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1444     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1445     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1448       return 1; // diagonal 1-3
1449
1450     return 2; // diagonal 2-4
1451   }
1452   return -1;
1453 }
1454
1455 namespace
1456 {
1457   // Methods of splitting volumes into tetra
1458
1459   const int theHexTo5_1[5*4+1] =
1460     {
1461       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1462     };
1463   const int theHexTo5_2[5*4+1] =
1464     {
1465       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1466     };
1467   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1468
1469   const int theHexTo6_1[6*4+1] =
1470     {
1471       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
1472     };
1473   const int theHexTo6_2[6*4+1] =
1474     {
1475       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
1476     };
1477   const int theHexTo6_3[6*4+1] =
1478     {
1479       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
1480     };
1481   const int theHexTo6_4[6*4+1] =
1482     {
1483       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
1484     };
1485   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1486
1487   const int thePyraTo2_1[2*4+1] =
1488     {
1489       0, 1, 2, 4,    0, 2, 3, 4,   -1
1490     };
1491   const int thePyraTo2_2[2*4+1] =
1492     {
1493       1, 2, 3, 4,    1, 3, 0, 4,   -1
1494     };
1495   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1496
1497   const int thePentaTo3_1[3*4+1] =
1498     {
1499       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1500     };
1501   const int thePentaTo3_2[3*4+1] =
1502     {
1503       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1504     };
1505   const int thePentaTo3_3[3*4+1] =
1506     {
1507       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1508     };
1509   const int thePentaTo3_4[3*4+1] =
1510     {
1511       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1512     };
1513   const int thePentaTo3_5[3*4+1] =
1514     {
1515       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1516     };
1517   const int thePentaTo3_6[3*4+1] =
1518     {
1519       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1520     };
1521   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1522                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1523
1524   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1525   {
1526     int _n1, _n2, _n3;
1527     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1528     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1529     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1530   };
1531   struct TSplitMethod
1532   {
1533     int        _nbTetra;
1534     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1535     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1536     bool       _ownConn;      //!< to delete _connectivity in destructor
1537     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1538
1539     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1540       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1541     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1542     bool hasFacet( const TTriangleFacet& facet ) const
1543     {
1544       const int* tetConn = _connectivity;
1545       for ( ; tetConn[0] >= 0; tetConn += 4 )
1546         if (( facet.contains( tetConn[0] ) +
1547               facet.contains( tetConn[1] ) +
1548               facet.contains( tetConn[2] ) +
1549               facet.contains( tetConn[3] )) == 3 )
1550           return true;
1551       return false;
1552     }
1553   };
1554
1555   //=======================================================================
1556   /*!
1557    * \brief return TSplitMethod for the given element
1558    */
1559   //=======================================================================
1560
1561   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1562   {
1563     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1564
1565     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1566     // an edge and a face barycenter; tertaherdons are based on triangles and
1567     // a volume barycenter
1568     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1569
1570     // Find out how adjacent volumes are split
1571
1572     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1573     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1574     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1575     {
1576       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1577       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1578       if ( nbNodes < 4 ) continue;
1579
1580       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1581       const int* nInd = vol.GetFaceNodesIndices( iF );
1582       if ( nbNodes == 4 )
1583       {
1584         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1585         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1586         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1587         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1588       }
1589       else
1590       {
1591         int iCom = 0; // common node of triangle faces to split into
1592         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1593         {
1594           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1595                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1596                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1597           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1598                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1599                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1600           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1601           {
1602             triaSplits.push_back( t012 );
1603             triaSplits.push_back( t023 );
1604             break;
1605           }
1606         }
1607       }
1608       if ( !triaSplits.empty() )
1609         hasAdjacentSplits = true;
1610     }
1611
1612     // Among variants of split method select one compliant with adjacent volumes
1613
1614     TSplitMethod method;
1615     if ( !vol.Element()->IsPoly() && !is24TetMode )
1616     {
1617       int nbVariants = 2, nbTet = 0;
1618       const int** connVariants = 0;
1619       switch ( vol.Element()->GetEntityType() )
1620       {
1621       case SMDSEntity_Hexa:
1622       case SMDSEntity_Quad_Hexa:
1623       case SMDSEntity_TriQuad_Hexa:
1624         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1625           connVariants = theHexTo5, nbTet = 5;
1626         else
1627           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1628         break;
1629       case SMDSEntity_Pyramid:
1630       case SMDSEntity_Quad_Pyramid:
1631         connVariants = thePyraTo2;  nbTet = 2;
1632         break;
1633       case SMDSEntity_Penta:
1634       case SMDSEntity_Quad_Penta:
1635         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1636         break;
1637       default:
1638         nbVariants = 0;
1639       }
1640       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1641       {
1642         // check method compliancy with adjacent tetras,
1643         // all found splits must be among facets of tetras described by this method
1644         method = TSplitMethod( nbTet, connVariants[variant] );
1645         if ( hasAdjacentSplits && method._nbTetra > 0 )
1646         {
1647           bool facetCreated = true;
1648           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1649           {
1650             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1651             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1652               facetCreated = method.hasFacet( *facet );
1653           }
1654           if ( !facetCreated )
1655             method = TSplitMethod(0); // incompatible method
1656         }
1657       }
1658     }
1659     if ( method._nbTetra < 1 )
1660     {
1661       // No standard method is applicable, use a generic solution:
1662       // each facet of a volume is split into triangles and
1663       // each of triangles and a volume barycenter form a tetrahedron.
1664
1665       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1666
1667       int* connectivity = new int[ maxTetConnSize + 1 ];
1668       method._connectivity = connectivity;
1669       method._ownConn = true;
1670       method._baryNode = !isHex27; // to create central node or not
1671
1672       int connSize = 0;
1673       int baryCenInd = vol.NbNodes() - int( isHex27 );
1674       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1675       {
1676         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1677         const int*   nInd = vol.GetFaceNodesIndices( iF );
1678         // find common node of triangle facets of tetra to create
1679         int iCommon = 0; // index in linear numeration
1680         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1681         if ( !triaSplits.empty() )
1682         {
1683           // by found facets
1684           const TTriangleFacet* facet = &triaSplits.front();
1685           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1686             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1687                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1688               break;
1689         }
1690         else if ( nbNodes > 3 && !is24TetMode )
1691         {
1692           // find the best method of splitting into triangles by aspect ratio
1693           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1694           map< double, int > badness2iCommon;
1695           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1696           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1697           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1698           {
1699             double badness = 0;
1700             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1701             {
1702               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1703                                       nodes[ iQ*((iLast-1)%nbNodes)],
1704                                       nodes[ iQ*((iLast  )%nbNodes)]);
1705               badness += getBadRate( &tria, aspectRatio );
1706             }
1707             badness2iCommon.insert( make_pair( badness, iCommon ));
1708           }
1709           // use iCommon with lowest badness
1710           iCommon = badness2iCommon.begin()->second;
1711         }
1712         if ( iCommon >= nbNodes )
1713           iCommon = 0; // something wrong
1714
1715         // fill connectivity of tetrahedra based on a current face
1716         int nbTet = nbNodes - 2;
1717         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1718         {
1719           int faceBaryCenInd;
1720           if ( isHex27 )
1721           {
1722             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1723             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1724           }
1725           else
1726           {
1727             method._faceBaryNode[ iF ] = 0;
1728             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1729           }
1730           nbTet = nbNodes;
1731           for ( int i = 0; i < nbTet; ++i )
1732           {
1733             int i1 = i, i2 = (i+1) % nbNodes;
1734             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1735             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1736             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1737             connectivity[ connSize++ ] = faceBaryCenInd;
1738             connectivity[ connSize++ ] = baryCenInd;
1739           }
1740         }
1741         else
1742         {
1743           for ( int i = 0; i < nbTet; ++i )
1744           {
1745             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1746             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1747             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1748             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1749             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1750             connectivity[ connSize++ ] = baryCenInd;
1751           }
1752         }
1753         method._nbTetra += nbTet;
1754
1755       } // loop on volume faces
1756
1757       connectivity[ connSize++ ] = -1;
1758
1759     } // end of generic solution
1760
1761     return method;
1762   }
1763   //================================================================================
1764   /*!
1765    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1766    */
1767   //================================================================================
1768
1769   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1770   {
1771     // find the tetrahedron including the three nodes of facet
1772     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1773     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1774     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1775     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1776     while ( volIt1->more() )
1777     {
1778       const SMDS_MeshElement* v = volIt1->next();
1779       SMDSAbs_EntityType type = v->GetEntityType();
1780       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1781         continue;
1782       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1783         continue; // medium node not allowed
1784       const int ind2 = v->GetNodeIndex( n2 );
1785       if ( ind2 < 0 || 3 < ind2 )
1786         continue;
1787       const int ind3 = v->GetNodeIndex( n3 );
1788       if ( ind3 < 0 || 3 < ind3 )
1789         continue;
1790       return true;
1791     }
1792     return false;
1793   }
1794
1795   //=======================================================================
1796   /*!
1797    * \brief A key of a face of volume
1798    */
1799   //=======================================================================
1800
1801   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1802   {
1803     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1804     {
1805       TIDSortedNodeSet sortedNodes;
1806       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1807       int nbNodes = vol.NbFaceNodes( iF );
1808       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1809       for ( int i = 0; i < nbNodes; i += iQ )
1810         sortedNodes.insert( fNodes[i] );
1811       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1812       first.first   = (*(n++))->GetID();
1813       first.second  = (*(n++))->GetID();
1814       second.first  = (*(n++))->GetID();
1815       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1816     }
1817   };
1818 } // namespace
1819
1820 //=======================================================================
1821 //function : SplitVolumesIntoTetra
1822 //purpose  : Split volume elements into tetrahedra.
1823 //=======================================================================
1824
1825 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1826                                               const int                theMethodFlags)
1827 {
1828   // std-like iterator on coordinates of nodes of mesh element
1829   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1830   NXyzIterator xyzEnd;
1831
1832   SMDS_VolumeTool    volTool;
1833   SMESH_MesherHelper helper( *GetMesh());
1834
1835   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1836   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1837
1838   SMESH_SequenceOfElemPtr newNodes, newElems;
1839
1840   // map face of volume to it's baricenrtic node
1841   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1842   double bc[3];
1843
1844   TIDSortedElemSet::const_iterator elem = theElems.begin();
1845   for ( ; elem != theElems.end(); ++elem )
1846   {
1847     if ( (*elem)->GetType() != SMDSAbs_Volume )
1848       continue;
1849     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1850     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1851       continue;
1852
1853     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1854
1855     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1856     if ( splitMethod._nbTetra < 1 ) continue;
1857
1858     // find submesh to add new tetras to
1859     if ( !subMesh || !subMesh->Contains( *elem ))
1860     {
1861       int shapeID = FindShape( *elem );
1862       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1863       subMesh = GetMeshDS()->MeshElements( shapeID );
1864     }
1865     int iQ;
1866     if ( (*elem)->IsQuadratic() )
1867     {
1868       iQ = 2;
1869       // add quadratic links to the helper
1870       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1871       {
1872         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1873         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1874         for ( int iN = 0; iN < nbN; iN += iQ )
1875           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1876       }
1877       helper.SetIsQuadratic( true );
1878     }
1879     else
1880     {
1881       iQ = 1;
1882       helper.SetIsQuadratic( false );
1883     }
1884     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1885     helper.SetElementsOnShape( true );
1886     if ( splitMethod._baryNode )
1887     {
1888       // make a node at barycenter
1889       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1890       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1891       nodes.push_back( gcNode );
1892       newNodes.Append( gcNode );
1893     }
1894     if ( !splitMethod._faceBaryNode.empty() )
1895     {
1896       // make or find baricentric nodes of faces
1897       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1898       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1899       {
1900         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1901           volFace2BaryNode.insert
1902           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1903         if ( !f_n->second )
1904         {
1905           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1906           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1907         }
1908         nodes.push_back( iF_n->second = f_n->second );
1909       }
1910     }
1911
1912     // make tetras
1913     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1914     const int* tetConn = splitMethod._connectivity;
1915     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1916       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1917                                                        nodes[ tetConn[1] ],
1918                                                        nodes[ tetConn[2] ],
1919                                                        nodes[ tetConn[3] ]));
1920
1921     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1922
1923     // Split faces on sides of the split volume
1924
1925     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1926     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1927     {
1928       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1929       if ( nbNodes < 4 ) continue;
1930
1931       // find an existing face
1932       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1933                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1934       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1935                                                                        /*noMedium=*/false))
1936       {
1937         // make triangles
1938         helper.SetElementsOnShape( false );
1939         vector< const SMDS_MeshElement* > triangles;
1940
1941         // find submesh to add new triangles in
1942         if ( !fSubMesh || !fSubMesh->Contains( face ))
1943         {
1944           int shapeID = FindShape( face );
1945           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1946         }
1947         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1948         if ( iF_n != splitMethod._faceBaryNode.end() )
1949         {
1950           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1951           {
1952             const SMDS_MeshNode* n1 = fNodes[iN];
1953             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1954             const SMDS_MeshNode *n3 = iF_n->second;
1955             if ( !volTool.IsFaceExternal( iF ))
1956               swap( n2, n3 );
1957             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1958
1959             if ( fSubMesh && n3->getshapeId() < 1 )
1960               fSubMesh->AddNode( n3 );
1961           }
1962         }
1963         else
1964         {
1965           // among possible triangles create ones discribed by split method
1966           const int* nInd = volTool.GetFaceNodesIndices( iF );
1967           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1968           int iCom = 0; // common node of triangle faces to split into
1969           list< TTriangleFacet > facets;
1970           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1971           {
1972             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1973                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1974                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1975             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1976                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1977                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1978             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1979             {
1980               facets.push_back( t012 );
1981               facets.push_back( t023 );
1982               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1983                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1984                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1985                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1986               break;
1987             }
1988           }
1989           list< TTriangleFacet >::iterator facet = facets.begin();
1990           for ( ; facet != facets.end(); ++facet )
1991           {
1992             if ( !volTool.IsFaceExternal( iF ))
1993               swap( facet->_n2, facet->_n3 );
1994             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1995                                                  volNodes[ facet->_n2 ],
1996                                                  volNodes[ facet->_n3 ]));
1997           }
1998         }
1999         for ( int i = 0; i < triangles.size(); ++i )
2000         {
2001           if ( !triangles[i] ) continue;
2002           if ( fSubMesh )
2003             fSubMesh->AddElement( triangles[i]);
2004           newElems.Append( triangles[i] );
2005         }
2006         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2007         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2008       }
2009
2010     } // loop on volume faces to split them into triangles
2011
2012     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2013
2014     if ( geomType == SMDSEntity_TriQuad_Hexa )
2015     {
2016       // remove medium nodes that could become free
2017       for ( int i = 20; i < volTool.NbNodes(); ++i )
2018         if ( volNodes[i]->NbInverseElements() == 0 )
2019           GetMeshDS()->RemoveNode( volNodes[i] );
2020     }
2021   } // loop on volumes to split
2022
2023   myLastCreatedNodes = newNodes;
2024   myLastCreatedElems = newElems;
2025 }
2026
2027 //=======================================================================
2028 //function : AddToSameGroups
2029 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2030 //=======================================================================
2031
2032 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2033                                         const SMDS_MeshElement* elemInGroups,
2034                                         SMESHDS_Mesh *          aMesh)
2035 {
2036   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2037   if (!groups.empty()) {
2038     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2039     for ( ; grIt != groups.end(); grIt++ ) {
2040       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2041       if ( group && group->Contains( elemInGroups ))
2042         group->SMDSGroup().Add( elemToAdd );
2043     }
2044   }
2045 }
2046
2047
2048 //=======================================================================
2049 //function : RemoveElemFromGroups
2050 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2051 //=======================================================================
2052 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2053                                              SMESHDS_Mesh *          aMesh)
2054 {
2055   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2056   if (!groups.empty())
2057   {
2058     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2059     for (; GrIt != groups.end(); GrIt++)
2060     {
2061       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2062       if (!grp || grp->IsEmpty()) continue;
2063       grp->SMDSGroup().Remove(removeelem);
2064     }
2065   }
2066 }
2067
2068 //================================================================================
2069 /*!
2070  * \brief Replace elemToRm by elemToAdd in the all groups
2071  */
2072 //================================================================================
2073
2074 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2075                                             const SMDS_MeshElement* elemToAdd,
2076                                             SMESHDS_Mesh *          aMesh)
2077 {
2078   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2079   if (!groups.empty()) {
2080     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2081     for ( ; grIt != groups.end(); grIt++ ) {
2082       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2083       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2084         group->SMDSGroup().Add( elemToAdd );
2085     }
2086   }
2087 }
2088
2089 //================================================================================
2090 /*!
2091  * \brief Replace elemToRm by elemToAdd in the all groups
2092  */
2093 //================================================================================
2094
2095 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2096                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2097                                             SMESHDS_Mesh *                         aMesh)
2098 {
2099   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2100   if (!groups.empty())
2101   {
2102     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2103     for ( ; grIt != groups.end(); grIt++ ) {
2104       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2105       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2106         for ( int i = 0; i < elemToAdd.size(); ++i )
2107           group->SMDSGroup().Add( elemToAdd[ i ] );
2108     }
2109   }
2110 }
2111
2112 //=======================================================================
2113 //function : QuadToTri
2114 //purpose  : Cut quadrangles into triangles.
2115 //           theCrit is used to select a diagonal to cut
2116 //=======================================================================
2117
2118 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2119                                   const bool         the13Diag)
2120 {
2121   myLastCreatedElems.Clear();
2122   myLastCreatedNodes.Clear();
2123
2124   MESSAGE( "::QuadToTri()" );
2125
2126   SMESHDS_Mesh * aMesh = GetMeshDS();
2127
2128   Handle(Geom_Surface) surface;
2129   SMESH_MesherHelper   helper( *GetMesh() );
2130
2131   TIDSortedElemSet::iterator itElem;
2132   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2133     const SMDS_MeshElement* elem = *itElem;
2134     if ( !elem || elem->GetType() != SMDSAbs_Face )
2135       continue;
2136     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2137     if(!isquad) continue;
2138
2139     if(elem->NbNodes()==4) {
2140       // retrieve element nodes
2141       const SMDS_MeshNode* aNodes [4];
2142       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2143       int i = 0;
2144       while ( itN->more() )
2145         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2146
2147       int aShapeId = FindShape( elem );
2148       const SMDS_MeshElement* newElem1 = 0;
2149       const SMDS_MeshElement* newElem2 = 0;
2150       if ( the13Diag ) {
2151         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2152         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2153       }
2154       else {
2155         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2156         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2157       }
2158       myLastCreatedElems.Append(newElem1);
2159       myLastCreatedElems.Append(newElem2);
2160       // put a new triangle on the same shape and add to the same groups
2161       if ( aShapeId )
2162         {
2163           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2164           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2165         }
2166       AddToSameGroups( newElem1, elem, aMesh );
2167       AddToSameGroups( newElem2, elem, aMesh );
2168       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2169       aMesh->RemoveElement( elem );
2170     }
2171
2172     // Quadratic quadrangle
2173
2174     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2175
2176       // get surface elem is on
2177       int aShapeId = FindShape( elem );
2178       if ( aShapeId != helper.GetSubShapeID() ) {
2179         surface.Nullify();
2180         TopoDS_Shape shape;
2181         if ( aShapeId > 0 )
2182           shape = aMesh->IndexToShape( aShapeId );
2183         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2184           TopoDS_Face face = TopoDS::Face( shape );
2185           surface = BRep_Tool::Surface( face );
2186           if ( !surface.IsNull() )
2187             helper.SetSubShape( shape );
2188         }
2189       }
2190
2191       const SMDS_MeshNode* aNodes [8];
2192       const SMDS_MeshNode* inFaceNode = 0;
2193       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2194       int i = 0;
2195       while ( itN->more() ) {
2196         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2197         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2198              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2199         {
2200           inFaceNode = aNodes[ i-1 ];
2201         }
2202       }
2203
2204       // find middle point for (0,1,2,3)
2205       // and create a node in this point;
2206       gp_XYZ p( 0,0,0 );
2207       if ( surface.IsNull() ) {
2208         for(i=0; i<4; i++)
2209           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2210         p /= 4;
2211       }
2212       else {
2213         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2214         gp_XY uv( 0,0 );
2215         for(i=0; i<4; i++)
2216           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2217         uv /= 4.;
2218         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2219       }
2220       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2221       myLastCreatedNodes.Append(newN);
2222
2223       // create a new element
2224       const SMDS_MeshElement* newElem1 = 0;
2225       const SMDS_MeshElement* newElem2 = 0;
2226       if ( the13Diag ) {
2227         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2228                                   aNodes[6], aNodes[7], newN );
2229         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2230                                   newN,      aNodes[4], aNodes[5] );
2231       }
2232       else {
2233         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2234                                   aNodes[7], aNodes[4], newN );
2235         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2236                                   newN,      aNodes[5], aNodes[6] );
2237       }
2238       myLastCreatedElems.Append(newElem1);
2239       myLastCreatedElems.Append(newElem2);
2240       // put a new triangle on the same shape and add to the same groups
2241       if ( aShapeId )
2242         {
2243           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2244           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2245         }
2246       AddToSameGroups( newElem1, elem, aMesh );
2247       AddToSameGroups( newElem2, elem, aMesh );
2248       aMesh->RemoveElement( elem );
2249     }
2250   }
2251
2252   return true;
2253 }
2254
2255 //=======================================================================
2256 //function : getAngle
2257 //purpose  :
2258 //=======================================================================
2259
2260 double getAngle(const SMDS_MeshElement * tr1,
2261                 const SMDS_MeshElement * tr2,
2262                 const SMDS_MeshNode *    n1,
2263                 const SMDS_MeshNode *    n2)
2264 {
2265   double angle = 2. * M_PI; // bad angle
2266
2267   // get normals
2268   SMESH::Controls::TSequenceOfXYZ P1, P2;
2269   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2270        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2271     return angle;
2272   gp_Vec N1,N2;
2273   if(!tr1->IsQuadratic())
2274     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2275   else
2276     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2277   if ( N1.SquareMagnitude() <= gp::Resolution() )
2278     return angle;
2279   if(!tr2->IsQuadratic())
2280     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2281   else
2282     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2283   if ( N2.SquareMagnitude() <= gp::Resolution() )
2284     return angle;
2285
2286   // find the first diagonal node n1 in the triangles:
2287   // take in account a diagonal link orientation
2288   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2289   for ( int t = 0; t < 2; t++ ) {
2290     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2291     int i = 0, iDiag = -1;
2292     while ( it->more()) {
2293       const SMDS_MeshElement *n = it->next();
2294       if ( n == n1 || n == n2 ) {
2295         if ( iDiag < 0)
2296           iDiag = i;
2297         else {
2298           if ( i - iDiag == 1 )
2299             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2300           else
2301             nFirst[ t ] = n;
2302           break;
2303         }
2304       }
2305       i++;
2306     }
2307   }
2308   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2309     N2.Reverse();
2310
2311   angle = N1.Angle( N2 );
2312   //SCRUTE( angle );
2313   return angle;
2314 }
2315
2316 // =================================================
2317 // class generating a unique ID for a pair of nodes
2318 // and able to return nodes by that ID
2319 // =================================================
2320 class LinkID_Gen {
2321 public:
2322
2323   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2324     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2325   {}
2326
2327   long GetLinkID (const SMDS_MeshNode * n1,
2328                   const SMDS_MeshNode * n2) const
2329   {
2330     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2331   }
2332
2333   bool GetNodes (const long             theLinkID,
2334                  const SMDS_MeshNode* & theNode1,
2335                  const SMDS_MeshNode* & theNode2) const
2336   {
2337     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2338     if ( !theNode1 ) return false;
2339     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2340     if ( !theNode2 ) return false;
2341     return true;
2342   }
2343
2344 private:
2345   LinkID_Gen();
2346   const SMESHDS_Mesh* myMesh;
2347   long                myMaxID;
2348 };
2349
2350
2351 //=======================================================================
2352 //function : TriToQuad
2353 //purpose  : Fuse neighbour triangles into quadrangles.
2354 //           theCrit is used to select a neighbour to fuse with.
2355 //           theMaxAngle is a max angle between element normals at which
2356 //           fusion is still performed.
2357 //=======================================================================
2358
2359 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2360                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2361                                   const double                         theMaxAngle)
2362 {
2363   myLastCreatedElems.Clear();
2364   myLastCreatedNodes.Clear();
2365
2366   MESSAGE( "::TriToQuad()" );
2367
2368   if ( !theCrit.get() )
2369     return false;
2370
2371   SMESHDS_Mesh * aMesh = GetMeshDS();
2372
2373   // Prepare data for algo: build
2374   // 1. map of elements with their linkIDs
2375   // 2. map of linkIDs with their elements
2376
2377   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2378   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2379   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2380   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2381
2382   TIDSortedElemSet::iterator itElem;
2383   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2384     const SMDS_MeshElement* elem = *itElem;
2385     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2386     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2387     if(!IsTria) continue;
2388
2389     // retrieve element nodes
2390     const SMDS_MeshNode* aNodes [4];
2391     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2392     int i = 0;
2393     while ( i<3 )
2394       aNodes[ i++ ] = cast2Node( itN->next() );
2395     aNodes[ 3 ] = aNodes[ 0 ];
2396
2397     // fill maps
2398     for ( i = 0; i < 3; i++ ) {
2399       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2400       // check if elements sharing a link can be fused
2401       itLE = mapLi_listEl.find( link );
2402       if ( itLE != mapLi_listEl.end() ) {
2403         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2404           continue;
2405         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2406         //if ( FindShape( elem ) != FindShape( elem2 ))
2407         //  continue; // do not fuse triangles laying on different shapes
2408         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2409           continue; // avoid making badly shaped quads
2410         (*itLE).second.push_back( elem );
2411       }
2412       else {
2413         mapLi_listEl[ link ].push_back( elem );
2414       }
2415       mapEl_setLi [ elem ].insert( link );
2416     }
2417   }
2418   // Clean the maps from the links shared by a sole element, ie
2419   // links to which only one element is bound in mapLi_listEl
2420
2421   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2422     int nbElems = (*itLE).second.size();
2423     if ( nbElems < 2  ) {
2424       const SMDS_MeshElement* elem = (*itLE).second.front();
2425       SMESH_TLink link = (*itLE).first;
2426       mapEl_setLi[ elem ].erase( link );
2427       if ( mapEl_setLi[ elem ].empty() )
2428         mapEl_setLi.erase( elem );
2429     }
2430   }
2431
2432   // Algo: fuse triangles into quadrangles
2433
2434   while ( ! mapEl_setLi.empty() ) {
2435     // Look for the start element:
2436     // the element having the least nb of shared links
2437     const SMDS_MeshElement* startElem = 0;
2438     int minNbLinks = 4;
2439     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2440       int nbLinks = (*itEL).second.size();
2441       if ( nbLinks < minNbLinks ) {
2442         startElem = (*itEL).first;
2443         minNbLinks = nbLinks;
2444         if ( minNbLinks == 1 )
2445           break;
2446       }
2447     }
2448
2449     // search elements to fuse starting from startElem or links of elements
2450     // fused earlyer - startLinks
2451     list< SMESH_TLink > startLinks;
2452     while ( startElem || !startLinks.empty() ) {
2453       while ( !startElem && !startLinks.empty() ) {
2454         // Get an element to start, by a link
2455         SMESH_TLink linkId = startLinks.front();
2456         startLinks.pop_front();
2457         itLE = mapLi_listEl.find( linkId );
2458         if ( itLE != mapLi_listEl.end() ) {
2459           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2460           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2461           for ( ; itE != listElem.end() ; itE++ )
2462             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2463               startElem = (*itE);
2464           mapLi_listEl.erase( itLE );
2465         }
2466       }
2467
2468       if ( startElem ) {
2469         // Get candidates to be fused
2470         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2471         const SMESH_TLink *link12, *link13;
2472         startElem = 0;
2473         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2474         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2475         ASSERT( !setLi.empty() );
2476         set< SMESH_TLink >::iterator itLi;
2477         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2478         {
2479           const SMESH_TLink & link = (*itLi);
2480           itLE = mapLi_listEl.find( link );
2481           if ( itLE == mapLi_listEl.end() )
2482             continue;
2483
2484           const SMDS_MeshElement* elem = (*itLE).second.front();
2485           if ( elem == tr1 )
2486             elem = (*itLE).second.back();
2487           mapLi_listEl.erase( itLE );
2488           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2489             continue;
2490           if ( tr2 ) {
2491             tr3 = elem;
2492             link13 = &link;
2493           }
2494           else {
2495             tr2 = elem;
2496             link12 = &link;
2497           }
2498
2499           // add other links of elem to list of links to re-start from
2500           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2501           set< SMESH_TLink >::iterator it;
2502           for ( it = links.begin(); it != links.end(); it++ ) {
2503             const SMESH_TLink& link2 = (*it);
2504             if ( link2 != link )
2505               startLinks.push_back( link2 );
2506           }
2507         }
2508
2509         // Get nodes of possible quadrangles
2510         const SMDS_MeshNode *n12 [4], *n13 [4];
2511         bool Ok12 = false, Ok13 = false;
2512         const SMDS_MeshNode *linkNode1, *linkNode2;
2513         if(tr2) {
2514           linkNode1 = link12->first;
2515           linkNode2 = link12->second;
2516           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2517             Ok12 = true;
2518         }
2519         if(tr3) {
2520           linkNode1 = link13->first;
2521           linkNode2 = link13->second;
2522           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2523             Ok13 = true;
2524         }
2525
2526         // Choose a pair to fuse
2527         if ( Ok12 && Ok13 ) {
2528           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2529           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2530           double aBadRate12 = getBadRate( &quad12, theCrit );
2531           double aBadRate13 = getBadRate( &quad13, theCrit );
2532           if (  aBadRate13 < aBadRate12 )
2533             Ok12 = false;
2534           else
2535             Ok13 = false;
2536         }
2537
2538         // Make quadrangles
2539         // and remove fused elems and removed links from the maps
2540         mapEl_setLi.erase( tr1 );
2541         if ( Ok12 ) {
2542           mapEl_setLi.erase( tr2 );
2543           mapLi_listEl.erase( *link12 );
2544           if(tr1->NbNodes()==3) {
2545             const SMDS_MeshElement* newElem = 0;
2546             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2547             myLastCreatedElems.Append(newElem);
2548             AddToSameGroups( newElem, tr1, aMesh );
2549             int aShapeId = tr1->getshapeId();
2550             if ( aShapeId )
2551               {
2552                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2553               }
2554             aMesh->RemoveElement( tr1 );
2555             aMesh->RemoveElement( tr2 );
2556           }
2557           else {
2558             const SMDS_MeshNode* N1 [6];
2559             const SMDS_MeshNode* N2 [6];
2560             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2561             // now we receive following N1 and N2 (using numeration as above image)
2562             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2563             // i.e. first nodes from both arrays determ new diagonal
2564             const SMDS_MeshNode* aNodes[8];
2565             aNodes[0] = N1[0];
2566             aNodes[1] = N1[1];
2567             aNodes[2] = N2[0];
2568             aNodes[3] = N2[1];
2569             aNodes[4] = N1[3];
2570             aNodes[5] = N2[5];
2571             aNodes[6] = N2[3];
2572             aNodes[7] = N1[5];
2573             const SMDS_MeshElement* newElem = 0;
2574             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2575                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2576             myLastCreatedElems.Append(newElem);
2577             AddToSameGroups( newElem, tr1, aMesh );
2578             int aShapeId = tr1->getshapeId();
2579             if ( aShapeId )
2580               {
2581                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2582               }
2583             aMesh->RemoveElement( tr1 );
2584             aMesh->RemoveElement( tr2 );
2585             // remove middle node (9)
2586             GetMeshDS()->RemoveNode( N1[4] );
2587           }
2588         }
2589         else if ( Ok13 ) {
2590           mapEl_setLi.erase( tr3 );
2591           mapLi_listEl.erase( *link13 );
2592           if(tr1->NbNodes()==3) {
2593             const SMDS_MeshElement* newElem = 0;
2594             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2595             myLastCreatedElems.Append(newElem);
2596             AddToSameGroups( newElem, tr1, aMesh );
2597             int aShapeId = tr1->getshapeId();
2598             if ( aShapeId )
2599               {
2600                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2601               }
2602             aMesh->RemoveElement( tr1 );
2603             aMesh->RemoveElement( tr3 );
2604           }
2605           else {
2606             const SMDS_MeshNode* N1 [6];
2607             const SMDS_MeshNode* N2 [6];
2608             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2609             // now we receive following N1 and N2 (using numeration as above image)
2610             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2611             // i.e. first nodes from both arrays determ new diagonal
2612             const SMDS_MeshNode* aNodes[8];
2613             aNodes[0] = N1[0];
2614             aNodes[1] = N1[1];
2615             aNodes[2] = N2[0];
2616             aNodes[3] = N2[1];
2617             aNodes[4] = N1[3];
2618             aNodes[5] = N2[5];
2619             aNodes[6] = N2[3];
2620             aNodes[7] = N1[5];
2621             const SMDS_MeshElement* newElem = 0;
2622             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2623                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2624             myLastCreatedElems.Append(newElem);
2625             AddToSameGroups( newElem, tr1, aMesh );
2626             int aShapeId = tr1->getshapeId();
2627             if ( aShapeId )
2628               {
2629                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2630               }
2631             aMesh->RemoveElement( tr1 );
2632             aMesh->RemoveElement( tr3 );
2633             // remove middle node (9)
2634             GetMeshDS()->RemoveNode( N1[4] );
2635           }
2636         }
2637
2638         // Next element to fuse: the rejected one
2639         if ( tr3 )
2640           startElem = Ok12 ? tr3 : tr2;
2641
2642       } // if ( startElem )
2643     } // while ( startElem || !startLinks.empty() )
2644   } // while ( ! mapEl_setLi.empty() )
2645
2646   return true;
2647 }
2648
2649
2650 /*#define DUMPSO(txt) \
2651 //  cout << txt << endl;
2652 //=============================================================================
2653 //
2654 //
2655 //
2656 //=============================================================================
2657 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2658 {
2659 if ( i1 == i2 )
2660 return;
2661 int tmp = idNodes[ i1 ];
2662 idNodes[ i1 ] = idNodes[ i2 ];
2663 idNodes[ i2 ] = tmp;
2664 gp_Pnt Ptmp = P[ i1 ];
2665 P[ i1 ] = P[ i2 ];
2666 P[ i2 ] = Ptmp;
2667 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2668 }
2669
2670 //=======================================================================
2671 //function : SortQuadNodes
2672 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2673 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2674 //           1 or 2 else 0.
2675 //=======================================================================
2676
2677 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2678 int               idNodes[] )
2679 {
2680   gp_Pnt P[4];
2681   int i;
2682   for ( i = 0; i < 4; i++ ) {
2683     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2684     if ( !n ) return 0;
2685     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2686   }
2687
2688   gp_Vec V1(P[0], P[1]);
2689   gp_Vec V2(P[0], P[2]);
2690   gp_Vec V3(P[0], P[3]);
2691
2692   gp_Vec Cross1 = V1 ^ V2;
2693   gp_Vec Cross2 = V2 ^ V3;
2694
2695   i = 0;
2696   if (Cross1.Dot(Cross2) < 0)
2697   {
2698     Cross1 = V2 ^ V1;
2699     Cross2 = V1 ^ V3;
2700
2701     if (Cross1.Dot(Cross2) < 0)
2702       i = 2;
2703     else
2704       i = 1;
2705     swap ( i, i + 1, idNodes, P );
2706
2707     //     for ( int ii = 0; ii < 4; ii++ ) {
2708     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2709     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2710     //     }
2711   }
2712   return i;
2713 }
2714
2715 //=======================================================================
2716 //function : SortHexaNodes
2717 //purpose  : Set 8 nodes of a hexahedron in a good order.
2718 //           Return success status
2719 //=======================================================================
2720
2721 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2722                                       int               idNodes[] )
2723 {
2724   gp_Pnt P[8];
2725   int i;
2726   DUMPSO( "INPUT: ========================================");
2727   for ( i = 0; i < 8; i++ ) {
2728     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2729     if ( !n ) return false;
2730     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2731     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2732   }
2733   DUMPSO( "========================================");
2734
2735
2736   set<int> faceNodes;  // ids of bottom face nodes, to be found
2737   set<int> checkedId1; // ids of tried 2-nd nodes
2738   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2739   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2740   int iMin, iLoop1 = 0;
2741
2742   // Loop to try the 2-nd nodes
2743
2744   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2745   {
2746     // Find not checked 2-nd node
2747     for ( i = 1; i < 8; i++ )
2748       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2749         int id1 = idNodes[i];
2750         swap ( 1, i, idNodes, P );
2751         checkedId1.insert ( id1 );
2752         break;
2753       }
2754
2755     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2756     // ie that all but meybe one (id3 which is on the same face) nodes
2757     // lay on the same side from the triangle plane.
2758
2759     bool manyInPlane = false; // more than 4 nodes lay in plane
2760     int iLoop2 = 0;
2761     while ( ++iLoop2 < 6 ) {
2762
2763       // get 1-2-3 plane coeffs
2764       Standard_Real A, B, C, D;
2765       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2766       if ( N.SquareMagnitude() > gp::Resolution() )
2767       {
2768         gp_Pln pln ( P[0], N );
2769         pln.Coefficients( A, B, C, D );
2770
2771         // find the node (iMin) closest to pln
2772         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2773         set<int> idInPln;
2774         for ( i = 3; i < 8; i++ ) {
2775           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2776           if ( fabs( dist[i] ) < minDist ) {
2777             minDist = fabs( dist[i] );
2778             iMin = i;
2779           }
2780           if ( fabs( dist[i] ) <= tol )
2781             idInPln.insert( idNodes[i] );
2782         }
2783
2784         // there should not be more than 4 nodes in bottom plane
2785         if ( idInPln.size() > 1 )
2786         {
2787           DUMPSO( "### idInPln.size() = " << idInPln.size());
2788           // idInPlane does not contain the first 3 nodes
2789           if ( manyInPlane || idInPln.size() == 5)
2790             return false; // all nodes in one plane
2791           manyInPlane = true;
2792
2793           // set the 1-st node to be not in plane
2794           for ( i = 3; i < 8; i++ ) {
2795             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2796               DUMPSO( "### Reset 0-th node");
2797               swap( 0, i, idNodes, P );
2798               break;
2799             }
2800           }
2801
2802           // reset to re-check second nodes
2803           leastDist = DBL_MAX;
2804           faceNodes.clear();
2805           checkedId1.clear();
2806           iLoop1 = 0;
2807           break; // from iLoop2;
2808         }
2809
2810         // check that the other 4 nodes are on the same side
2811         bool sameSide = true;
2812         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2813         for ( i = 3; sameSide && i < 8; i++ ) {
2814           if ( i != iMin )
2815             sameSide = ( isNeg == dist[i] <= 0.);
2816         }
2817
2818         // keep best solution
2819         if ( sameSide && minDist < leastDist ) {
2820           leastDist = minDist;
2821           faceNodes.clear();
2822           faceNodes.insert( idNodes[ 1 ] );
2823           faceNodes.insert( idNodes[ 2 ] );
2824           faceNodes.insert( idNodes[ iMin ] );
2825           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2826                   << " leastDist = " << leastDist);
2827           if ( leastDist <= DBL_MIN )
2828             break;
2829         }
2830       }
2831
2832       // set next 3-d node to check
2833       int iNext = 2 + iLoop2;
2834       if ( iNext < 8 ) {
2835         DUMPSO( "Try 2-nd");
2836         swap ( 2, iNext, idNodes, P );
2837       }
2838     } // while ( iLoop2 < 6 )
2839   } // iLoop1
2840
2841   if ( faceNodes.empty() ) return false;
2842
2843   // Put the faceNodes in proper places
2844   for ( i = 4; i < 8; i++ ) {
2845     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2846       // find a place to put
2847       int iTo = 1;
2848       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2849         iTo++;
2850       DUMPSO( "Set faceNodes");
2851       swap ( iTo, i, idNodes, P );
2852     }
2853   }
2854
2855
2856   // Set nodes of the found bottom face in good order
2857   DUMPSO( " Found bottom face: ");
2858   i = SortQuadNodes( theMesh, idNodes );
2859   if ( i ) {
2860     gp_Pnt Ptmp = P[ i ];
2861     P[ i ] = P[ i+1 ];
2862     P[ i+1 ] = Ptmp;
2863   }
2864   //   else
2865   //     for ( int ii = 0; ii < 4; ii++ ) {
2866   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2867   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2868   //    }
2869
2870   // Gravity center of the top and bottom faces
2871   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2872   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2873
2874   // Get direction from the bottom to the top face
2875   gp_Vec upDir ( aGCb, aGCt );
2876   Standard_Real upDirSize = upDir.Magnitude();
2877   if ( upDirSize <= gp::Resolution() ) return false;
2878   upDir / upDirSize;
2879
2880   // Assure that the bottom face normal points up
2881   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2882   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2883   if ( Nb.Dot( upDir ) < 0 ) {
2884     DUMPSO( "Reverse bottom face");
2885     swap( 1, 3, idNodes, P );
2886   }
2887
2888   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2889   Standard_Real minDist = DBL_MAX;
2890   for ( i = 4; i < 8; i++ ) {
2891     // projection of P[i] to the plane defined by P[0] and upDir
2892     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2893     Standard_Real sqDist = P[0].SquareDistance( Pp );
2894     if ( sqDist < minDist ) {
2895       minDist = sqDist;
2896       iMin = i;
2897     }
2898   }
2899   DUMPSO( "Set 4-th");
2900   swap ( 4, iMin, idNodes, P );
2901
2902   // Set nodes of the top face in good order
2903   DUMPSO( "Sort top face");
2904   i = SortQuadNodes( theMesh, &idNodes[4] );
2905   if ( i ) {
2906     i += 4;
2907     gp_Pnt Ptmp = P[ i ];
2908     P[ i ] = P[ i+1 ];
2909     P[ i+1 ] = Ptmp;
2910   }
2911
2912   // Assure that direction of the top face normal is from the bottom face
2913   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2914   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2915   if ( Nt.Dot( upDir ) < 0 ) {
2916     DUMPSO( "Reverse top face");
2917     swap( 5, 7, idNodes, P );
2918   }
2919
2920   //   DUMPSO( "OUTPUT: ========================================");
2921   //   for ( i = 0; i < 8; i++ ) {
2922   //     float *p = ugrid->GetPoint(idNodes[i]);
2923   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2924   //   }
2925
2926   return true;
2927 }*/
2928
2929 //================================================================================
2930 /*!
2931  * \brief Return nodes linked to the given one
2932  * \param theNode - the node
2933  * \param linkedNodes - the found nodes
2934  * \param type - the type of elements to check
2935  *
2936  * Medium nodes are ignored
2937  */
2938 //================================================================================
2939
2940 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2941                                        TIDSortedElemSet &   linkedNodes,
2942                                        SMDSAbs_ElementType  type )
2943 {
2944   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2945   while ( elemIt->more() )
2946   {
2947     const SMDS_MeshElement* elem = elemIt->next();
2948     if(elem->GetType() == SMDSAbs_0DElement)
2949       continue;
2950
2951     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2952     if ( elem->GetType() == SMDSAbs_Volume )
2953     {
2954       SMDS_VolumeTool vol( elem );
2955       while ( nodeIt->more() ) {
2956         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2957         if ( theNode != n && vol.IsLinked( theNode, n ))
2958           linkedNodes.insert( n );
2959       }
2960     }
2961     else
2962     {
2963       for ( int i = 0; nodeIt->more(); ++i ) {
2964         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2965         if ( n == theNode ) {
2966           int iBefore = i - 1;
2967           int iAfter  = i + 1;
2968           if ( elem->IsQuadratic() ) {
2969             int nb = elem->NbNodes() / 2;
2970             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2971             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2972           }
2973           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2974           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2975         }
2976       }
2977     }
2978   }
2979 }
2980
2981 //=======================================================================
2982 //function : laplacianSmooth
2983 //purpose  : pulls theNode toward the center of surrounding nodes directly
2984 //           connected to that node along an element edge
2985 //=======================================================================
2986
2987 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2988                      const Handle(Geom_Surface)&          theSurface,
2989                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2990 {
2991   // find surrounding nodes
2992
2993   TIDSortedElemSet nodeSet;
2994   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2995
2996   // compute new coodrs
2997
2998   double coord[] = { 0., 0., 0. };
2999   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3000   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3001     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3002     if ( theSurface.IsNull() ) { // smooth in 3D
3003       coord[0] += node->X();
3004       coord[1] += node->Y();
3005       coord[2] += node->Z();
3006     }
3007     else { // smooth in 2D
3008       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3009       gp_XY* uv = theUVMap[ node ];
3010       coord[0] += uv->X();
3011       coord[1] += uv->Y();
3012     }
3013   }
3014   int nbNodes = nodeSet.size();
3015   if ( !nbNodes )
3016     return;
3017   coord[0] /= nbNodes;
3018   coord[1] /= nbNodes;
3019
3020   if ( !theSurface.IsNull() ) {
3021     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3022     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3023     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3024     coord[0] = p3d.X();
3025     coord[1] = p3d.Y();
3026     coord[2] = p3d.Z();
3027   }
3028   else
3029     coord[2] /= nbNodes;
3030
3031   // move node
3032
3033   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3034 }
3035
3036 //=======================================================================
3037 //function : centroidalSmooth
3038 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3039 //           surrounding elements
3040 //=======================================================================
3041
3042 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3043                       const Handle(Geom_Surface)&          theSurface,
3044                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3045 {
3046   gp_XYZ aNewXYZ(0.,0.,0.);
3047   SMESH::Controls::Area anAreaFunc;
3048   double totalArea = 0.;
3049   int nbElems = 0;
3050
3051   // compute new XYZ
3052
3053   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3054   while ( elemIt->more() )
3055   {
3056     const SMDS_MeshElement* elem = elemIt->next();
3057     nbElems++;
3058
3059     gp_XYZ elemCenter(0.,0.,0.);
3060     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3061     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3062     int nn = elem->NbNodes();
3063     if(elem->IsQuadratic()) nn = nn/2;
3064     int i=0;
3065     //while ( itN->more() ) {
3066     while ( i<nn ) {
3067       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3068       i++;
3069       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3070       aNodePoints.push_back( aP );
3071       if ( !theSurface.IsNull() ) { // smooth in 2D
3072         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3073         gp_XY* uv = theUVMap[ aNode ];
3074         aP.SetCoord( uv->X(), uv->Y(), 0. );
3075       }
3076       elemCenter += aP;
3077     }
3078     double elemArea = anAreaFunc.GetValue( aNodePoints );
3079     totalArea += elemArea;
3080     elemCenter /= nn;
3081     aNewXYZ += elemCenter * elemArea;
3082   }
3083   aNewXYZ /= totalArea;
3084   if ( !theSurface.IsNull() ) {
3085     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3086     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3087   }
3088
3089   // move node
3090
3091   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3092 }
3093
3094 //=======================================================================
3095 //function : getClosestUV
3096 //purpose  : return UV of closest projection
3097 //=======================================================================
3098
3099 static bool getClosestUV (Extrema_GenExtPS& projector,
3100                           const gp_Pnt&     point,
3101                           gp_XY &           result)
3102 {
3103   projector.Perform( point );
3104   if ( projector.IsDone() ) {
3105     double u, v, minVal = DBL_MAX;
3106     for ( int i = projector.NbExt(); i > 0; i-- )
3107 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3108       if ( projector.SquareDistance( i ) < minVal ) {
3109         minVal = projector.SquareDistance( i );
3110 #else
3111       if ( projector.Value( i ) < minVal ) {
3112         minVal = projector.Value( i );
3113 #endif
3114         projector.Point( i ).Parameter( u, v );
3115       }
3116     result.SetCoord( u, v );
3117     return true;
3118   }
3119   return false;
3120 }
3121
3122 //=======================================================================
3123 //function : Smooth
3124 //purpose  : Smooth theElements during theNbIterations or until a worst
3125 //           element has aspect ratio <= theTgtAspectRatio.
3126 //           Aspect Ratio varies in range [1.0, inf].
3127 //           If theElements is empty, the whole mesh is smoothed.
3128 //           theFixedNodes contains additionally fixed nodes. Nodes built
3129 //           on edges and boundary nodes are always fixed.
3130 //=======================================================================
3131
3132 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3133                                set<const SMDS_MeshNode*> & theFixedNodes,
3134                                const SmoothMethod          theSmoothMethod,
3135                                const int                   theNbIterations,
3136                                double                      theTgtAspectRatio,
3137                                const bool                  the2D)
3138 {
3139   myLastCreatedElems.Clear();
3140   myLastCreatedNodes.Clear();
3141
3142   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3143
3144   if ( theTgtAspectRatio < 1.0 )
3145     theTgtAspectRatio = 1.0;
3146
3147   const double disttol = 1.e-16;
3148
3149   SMESH::Controls::AspectRatio aQualityFunc;
3150
3151   SMESHDS_Mesh* aMesh = GetMeshDS();
3152
3153   if ( theElems.empty() ) {
3154     // add all faces to theElems
3155     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3156     while ( fIt->more() ) {
3157       const SMDS_MeshElement* face = fIt->next();
3158       theElems.insert( theElems.end(), face );
3159     }
3160   }
3161   // get all face ids theElems are on
3162   set< int > faceIdSet;
3163   TIDSortedElemSet::iterator itElem;
3164   if ( the2D )
3165     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3166       int fId = FindShape( *itElem );
3167       // check that corresponding submesh exists and a shape is face
3168       if (fId &&
3169           faceIdSet.find( fId ) == faceIdSet.end() &&
3170           aMesh->MeshElements( fId )) {
3171         TopoDS_Shape F = aMesh->IndexToShape( fId );
3172         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3173           faceIdSet.insert( fId );
3174       }
3175     }
3176   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3177
3178   // ===============================================
3179   // smooth elements on each TopoDS_Face separately
3180   // ===============================================
3181
3182   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3183   for ( ; fId != faceIdSet.rend(); ++fId ) {
3184     // get face surface and submesh
3185     Handle(Geom_Surface) surface;
3186     SMESHDS_SubMesh* faceSubMesh = 0;
3187     TopoDS_Face face;
3188     double fToler2 = 0, f,l;
3189     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3190     bool isUPeriodic = false, isVPeriodic = false;
3191     if ( *fId ) {
3192       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3193       surface = BRep_Tool::Surface( face );
3194       faceSubMesh = aMesh->MeshElements( *fId );
3195       fToler2 = BRep_Tool::Tolerance( face );
3196       fToler2 *= fToler2 * 10.;
3197       isUPeriodic = surface->IsUPeriodic();
3198       if ( isUPeriodic )
3199         surface->UPeriod();
3200       isVPeriodic = surface->IsVPeriodic();
3201       if ( isVPeriodic )
3202         surface->VPeriod();
3203       surface->Bounds( u1, u2, v1, v2 );
3204     }
3205     // ---------------------------------------------------------
3206     // for elements on a face, find movable and fixed nodes and
3207     // compute UV for them
3208     // ---------------------------------------------------------
3209     bool checkBoundaryNodes = false;
3210     bool isQuadratic = false;
3211     set<const SMDS_MeshNode*> setMovableNodes;
3212     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3213     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3214     list< const SMDS_MeshElement* > elemsOnFace;
3215
3216     Extrema_GenExtPS projector;
3217     GeomAdaptor_Surface surfAdaptor;
3218     if ( !surface.IsNull() ) {
3219       surfAdaptor.Load( surface );
3220       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3221     }
3222     int nbElemOnFace = 0;
3223     itElem = theElems.begin();
3224     // loop on not yet smoothed elements: look for elems on a face
3225     while ( itElem != theElems.end() ) {
3226       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3227         break; // all elements found
3228
3229       const SMDS_MeshElement* elem = *itElem;
3230       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3231            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3232         ++itElem;
3233         continue;
3234       }
3235       elemsOnFace.push_back( elem );
3236       theElems.erase( itElem++ );
3237       nbElemOnFace++;
3238
3239       if ( !isQuadratic )
3240         isQuadratic = elem->IsQuadratic();
3241
3242       // get movable nodes of elem
3243       const SMDS_MeshNode* node;
3244       SMDS_TypeOfPosition posType;
3245       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3246       int nn = 0, nbn =  elem->NbNodes();
3247       if(elem->IsQuadratic())
3248         nbn = nbn/2;
3249       while ( nn++ < nbn ) {
3250         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3251         const SMDS_PositionPtr& pos = node->GetPosition();
3252         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3253         if (posType != SMDS_TOP_EDGE &&
3254             posType != SMDS_TOP_VERTEX &&
3255             theFixedNodes.find( node ) == theFixedNodes.end())
3256         {
3257           // check if all faces around the node are on faceSubMesh
3258           // because a node on edge may be bound to face
3259           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3260           bool all = true;
3261           if ( faceSubMesh ) {
3262             while ( eIt->more() && all ) {
3263               const SMDS_MeshElement* e = eIt->next();
3264               all = faceSubMesh->Contains( e );
3265             }
3266           }
3267           if ( all )
3268             setMovableNodes.insert( node );
3269           else
3270             checkBoundaryNodes = true;
3271         }
3272         if ( posType == SMDS_TOP_3DSPACE )
3273           checkBoundaryNodes = true;
3274       }
3275
3276       if ( surface.IsNull() )
3277         continue;
3278
3279       // get nodes to check UV
3280       list< const SMDS_MeshNode* > uvCheckNodes;
3281       itN = elem->nodesIterator();
3282       nn = 0; nbn =  elem->NbNodes();
3283       if(elem->IsQuadratic())
3284         nbn = nbn/2;
3285       while ( nn++ < nbn ) {
3286         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3287         if ( uvMap.find( node ) == uvMap.end() )
3288           uvCheckNodes.push_back( node );
3289         // add nodes of elems sharing node
3290         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3291         //         while ( eIt->more() ) {
3292         //           const SMDS_MeshElement* e = eIt->next();
3293         //           if ( e != elem ) {
3294         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3295         //             while ( nIt->more() ) {
3296         //               const SMDS_MeshNode* n =
3297         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3298         //               if ( uvMap.find( n ) == uvMap.end() )
3299         //                 uvCheckNodes.push_back( n );
3300         //             }
3301         //           }
3302         //         }
3303       }
3304       // check UV on face
3305       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3306       for ( ; n != uvCheckNodes.end(); ++n ) {
3307         node = *n;
3308         gp_XY uv( 0, 0 );
3309         const SMDS_PositionPtr& pos = node->GetPosition();
3310         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3311         // get existing UV
3312         switch ( posType ) {
3313         case SMDS_TOP_FACE: {
3314           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3315           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3316           break;
3317         }
3318         case SMDS_TOP_EDGE: {
3319           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3320           Handle(Geom2d_Curve) pcurve;
3321           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3322             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3323           if ( !pcurve.IsNull() ) {
3324             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3325             uv = pcurve->Value( u ).XY();
3326           }
3327           break;
3328         }
3329         case SMDS_TOP_VERTEX: {
3330           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3331           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3332             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3333           break;
3334         }
3335         default:;
3336         }
3337         // check existing UV
3338         bool project = true;
3339         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3340         double dist1 = DBL_MAX, dist2 = 0;
3341         if ( posType != SMDS_TOP_3DSPACE ) {
3342           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3343           project = dist1 > fToler2;
3344         }
3345         if ( project ) { // compute new UV
3346           gp_XY newUV;
3347           if ( !getClosestUV( projector, pNode, newUV )) {
3348             MESSAGE("Node Projection Failed " << node);
3349           }
3350           else {
3351             if ( isUPeriodic )
3352               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3353             if ( isVPeriodic )
3354               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3355             // check new UV
3356             if ( posType != SMDS_TOP_3DSPACE )
3357               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3358             if ( dist2 < dist1 )
3359               uv = newUV;
3360           }
3361         }
3362         // store UV in the map
3363         listUV.push_back( uv );
3364         uvMap.insert( make_pair( node, &listUV.back() ));
3365       }
3366     } // loop on not yet smoothed elements
3367
3368     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3369       checkBoundaryNodes = true;
3370
3371     // fix nodes on mesh boundary
3372
3373     if ( checkBoundaryNodes ) {
3374       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3375       map< SMESH_TLink, int >::iterator link_nb;
3376       // put all elements links to linkNbMap
3377       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3378       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3379         const SMDS_MeshElement* elem = (*elemIt);
3380         int nbn =  elem->NbCornerNodes();
3381         // loop on elem links: insert them in linkNbMap
3382         for ( int iN = 0; iN < nbn; ++iN ) {
3383           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3384           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3385           SMESH_TLink link( n1, n2 );
3386           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3387           link_nb->second++;
3388         }
3389       }
3390       // remove nodes that are in links encountered only once from setMovableNodes
3391       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3392         if ( link_nb->second == 1 ) {
3393           setMovableNodes.erase( link_nb->first.node1() );
3394           setMovableNodes.erase( link_nb->first.node2() );
3395         }
3396       }
3397     }
3398
3399     // -----------------------------------------------------
3400     // for nodes on seam edge, compute one more UV ( uvMap2 );
3401     // find movable nodes linked to nodes on seam and which
3402     // are to be smoothed using the second UV ( uvMap2 )
3403     // -----------------------------------------------------
3404
3405     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3406     if ( !surface.IsNull() ) {
3407       TopExp_Explorer eExp( face, TopAbs_EDGE );
3408       for ( ; eExp.More(); eExp.Next() ) {
3409         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3410         if ( !BRep_Tool::IsClosed( edge, face ))
3411           continue;
3412         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3413         if ( !sm ) continue;
3414         // find out which parameter varies for a node on seam
3415         double f,l;
3416         gp_Pnt2d uv1, uv2;
3417         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3418         if ( pcurve.IsNull() ) continue;
3419         uv1 = pcurve->Value( f );
3420         edge.Reverse();
3421         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3422         if ( pcurve.IsNull() ) continue;
3423         uv2 = pcurve->Value( f );
3424         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3425         // assure uv1 < uv2
3426         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3427           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3428         }
3429         // get nodes on seam and its vertices
3430         list< const SMDS_MeshNode* > seamNodes;
3431         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3432         while ( nSeamIt->more() ) {
3433           const SMDS_MeshNode* node = nSeamIt->next();
3434           if ( !isQuadratic || !IsMedium( node ))
3435             seamNodes.push_back( node );
3436         }
3437         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3438         for ( ; vExp.More(); vExp.Next() ) {
3439           sm = aMesh->MeshElements( vExp.Current() );
3440           if ( sm ) {
3441             nSeamIt = sm->GetNodes();
3442             while ( nSeamIt->more() )
3443               seamNodes.push_back( nSeamIt->next() );
3444           }
3445         }
3446         // loop on nodes on seam
3447         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3448         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3449           const SMDS_MeshNode* nSeam = *noSeIt;
3450           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3451           if ( n_uv == uvMap.end() )
3452             continue;
3453           // set the first UV
3454           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3455           // set the second UV
3456           listUV.push_back( *n_uv->second );
3457           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3458           if ( uvMap2.empty() )
3459             uvMap2 = uvMap; // copy the uvMap contents
3460           uvMap2[ nSeam ] = &listUV.back();
3461
3462           // collect movable nodes linked to ones on seam in nodesNearSeam
3463           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3464           while ( eIt->more() ) {
3465             const SMDS_MeshElement* e = eIt->next();
3466             int nbUseMap1 = 0, nbUseMap2 = 0;
3467             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3468             int nn = 0, nbn =  e->NbNodes();
3469             if(e->IsQuadratic()) nbn = nbn/2;
3470             while ( nn++ < nbn )
3471             {
3472               const SMDS_MeshNode* n =
3473                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3474               if (n == nSeam ||
3475                   setMovableNodes.find( n ) == setMovableNodes.end() )
3476                 continue;
3477               // add only nodes being closer to uv2 than to uv1
3478               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3479                            0.5 * ( n->Y() + nSeam->Y() ),
3480                            0.5 * ( n->Z() + nSeam->Z() ));
3481               gp_XY uv;
3482               getClosestUV( projector, pMid, uv );
3483               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3484                 nodesNearSeam.insert( n );
3485                 nbUseMap2++;
3486               }
3487               else
3488                 nbUseMap1++;
3489             }
3490             // for centroidalSmooth all element nodes must
3491             // be on one side of a seam
3492             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3493               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3494               nn = 0;
3495               while ( nn++ < nbn ) {
3496                 const SMDS_MeshNode* n =
3497                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3498                 setMovableNodes.erase( n );
3499               }
3500             }
3501           }
3502         } // loop on nodes on seam
3503       } // loop on edge of a face
3504     } // if ( !face.IsNull() )
3505
3506     if ( setMovableNodes.empty() ) {
3507       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3508       continue; // goto next face
3509     }
3510
3511     // -------------
3512     // SMOOTHING //
3513     // -------------
3514
3515     int it = -1;
3516     double maxRatio = -1., maxDisplacement = -1.;
3517     set<const SMDS_MeshNode*>::iterator nodeToMove;
3518     for ( it = 0; it < theNbIterations; it++ ) {
3519       maxDisplacement = 0.;
3520       nodeToMove = setMovableNodes.begin();
3521       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3522         const SMDS_MeshNode* node = (*nodeToMove);
3523         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3524
3525         // smooth
3526         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3527         if ( theSmoothMethod == LAPLACIAN )
3528           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3529         else
3530           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3531
3532         // node displacement
3533         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3534         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3535         if ( aDispl > maxDisplacement )
3536           maxDisplacement = aDispl;
3537       }
3538       // no node movement => exit
3539       //if ( maxDisplacement < 1.e-16 ) {
3540       if ( maxDisplacement < disttol ) {
3541         MESSAGE("-- no node movement --");
3542         break;
3543       }
3544
3545       // check elements quality
3546       maxRatio  = 0;
3547       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3548       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3549         const SMDS_MeshElement* elem = (*elemIt);
3550         if ( !elem || elem->GetType() != SMDSAbs_Face )
3551           continue;
3552         SMESH::Controls::TSequenceOfXYZ aPoints;
3553         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3554           double aValue = aQualityFunc.GetValue( aPoints );
3555           if ( aValue > maxRatio )
3556             maxRatio = aValue;
3557         }
3558       }
3559       if ( maxRatio <= theTgtAspectRatio ) {
3560         MESSAGE("-- quality achived --");
3561         break;
3562       }
3563       if (it+1 == theNbIterations) {
3564         MESSAGE("-- Iteration limit exceeded --");
3565       }
3566     } // smoothing iterations
3567
3568     MESSAGE(" Face id: " << *fId <<
3569             " Nb iterstions: " << it <<
3570             " Displacement: " << maxDisplacement <<
3571             " Aspect Ratio " << maxRatio);
3572
3573     // ---------------------------------------
3574     // new nodes positions are computed,
3575     // record movement in DS and set new UV
3576     // ---------------------------------------
3577     nodeToMove = setMovableNodes.begin();
3578     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3579       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3580       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3581       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3582       if ( node_uv != uvMap.end() ) {
3583         gp_XY* uv = node_uv->second;
3584         node->SetPosition
3585           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3586       }
3587     }
3588
3589     // move medium nodes of quadratic elements
3590     if ( isQuadratic )
3591     {
3592       SMESH_MesherHelper helper( *GetMesh() );
3593       if ( !face.IsNull() )
3594         helper.SetSubShape( face );
3595       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3596       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3597         const SMDS_VtkFace* QF =
3598           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3599         if(QF && QF->IsQuadratic()) {
3600           vector<const SMDS_MeshNode*> Ns;
3601           Ns.reserve(QF->NbNodes()+1);
3602           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3603           while ( anIter->more() )
3604             Ns.push_back( cast2Node(anIter->next()) );
3605           Ns.push_back( Ns[0] );
3606           double x, y, z;
3607           for(int i=0; i<QF->NbNodes(); i=i+2) {
3608             if ( !surface.IsNull() ) {
3609               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3610               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3611               gp_XY uv = ( uv1 + uv2 ) / 2.;
3612               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3613               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3614             }
3615             else {
3616               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3617               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3618               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3619             }
3620             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3621                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3622                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3623               // we have to move i+1 node
3624               aMesh->MoveNode( Ns[i+1], x, y, z );
3625             }
3626           }
3627         }
3628       }
3629     }
3630
3631   } // loop on face ids
3632
3633 }
3634
3635 //=======================================================================
3636 //function : isReverse
3637 //purpose  : Return true if normal of prevNodes is not co-directied with
3638 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3639 //           iNotSame is where prevNodes and nextNodes are different.
3640 //           If result is true then future volume orientation is OK
3641 //=======================================================================
3642
3643 static bool isReverse(const SMDS_MeshElement*             face,
3644                       const vector<const SMDS_MeshNode*>& prevNodes,
3645                       const vector<const SMDS_MeshNode*>& nextNodes,
3646                       const int                           iNotSame)
3647 {
3648
3649   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3650   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3651   gp_XYZ extrDir( pN - pP ), faceNorm;
3652   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3653
3654   return faceNorm * extrDir < 0.0;
3655 }
3656
3657 //=======================================================================
3658 /*!
3659  * \brief Create elements by sweeping an element
3660  * \param elem - element to sweep
3661  * \param newNodesItVec - nodes generated from each node of the element
3662  * \param newElems - generated elements
3663  * \param nbSteps - number of sweeping steps
3664  * \param srcElements - to append elem for each generated element
3665  */
3666 //=======================================================================
3667
3668 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3669                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3670                                     list<const SMDS_MeshElement*>&        newElems,
3671                                     const int                             nbSteps,
3672                                     SMESH_SequenceOfElemPtr&              srcElements)
3673 {
3674   //MESSAGE("sweepElement " << nbSteps);
3675   SMESHDS_Mesh* aMesh = GetMeshDS();
3676
3677   const int           nbNodes = elem->NbNodes();
3678   const int         nbCorners = elem->NbCornerNodes();
3679   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3680                                                           polyhedron creation !!! */
3681   // Loop on elem nodes:
3682   // find new nodes and detect same nodes indices
3683   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3684   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3685   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3686   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3687
3688   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3689   vector<int> sames(nbNodes);
3690   vector<bool> isSingleNode(nbNodes);
3691
3692   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3693     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3694     const SMDS_MeshNode*                         node = nnIt->first;
3695     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3696     if ( listNewNodes.empty() )
3697       return;
3698
3699     itNN   [ iNode ] = listNewNodes.begin();
3700     prevNod[ iNode ] = node;
3701     nextNod[ iNode ] = listNewNodes.front();
3702
3703     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3704                                                              corner node of linear */
3705     if ( prevNod[ iNode ] != nextNod [ iNode ])
3706       nbDouble += !isSingleNode[iNode];
3707
3708     if( iNode < nbCorners ) { // check corners only
3709       if ( prevNod[ iNode ] == nextNod [ iNode ])
3710         sames[nbSame++] = iNode;
3711       else
3712         iNotSameNode = iNode;
3713     }
3714   }
3715
3716   if ( nbSame == nbNodes || nbSame > 2) {
3717     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3718     return;
3719   }
3720
3721   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3722   {
3723     // fix nodes order to have bottom normal external
3724     if ( baseType == SMDSEntity_Polygon )
3725     {
3726       std::reverse( itNN.begin(), itNN.end() );
3727       std::reverse( prevNod.begin(), prevNod.end() );
3728       std::reverse( midlNod.begin(), midlNod.end() );
3729       std::reverse( nextNod.begin(), nextNod.end() );
3730       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3731     }
3732     else
3733     {
3734       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3735       SMDS_MeshCell::applyInterlace( ind, itNN );
3736       SMDS_MeshCell::applyInterlace( ind, prevNod );
3737       SMDS_MeshCell::applyInterlace( ind, nextNod );
3738       SMDS_MeshCell::applyInterlace( ind, midlNod );
3739       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3740       if ( nbSame > 0 )
3741       {
3742         sames[nbSame] = iNotSameNode;
3743         for ( int j = 0; j <= nbSame; ++j )
3744           for ( size_t i = 0; i < ind.size(); ++i )
3745             if ( ind[i] == sames[j] )
3746             {
3747               sames[j] = i;
3748               break;
3749             }
3750         iNotSameNode = sames[nbSame];
3751       }
3752     }
3753   }
3754
3755   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3756   if ( nbSame > 0 ) {
3757     iSameNode    = sames[ nbSame-1 ];
3758     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3759     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3760     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3761   }
3762
3763   // make new elements
3764   for (int iStep = 0; iStep < nbSteps; iStep++ )
3765   {
3766     // get next nodes
3767     for ( iNode = 0; iNode < nbNodes; iNode++ )
3768     {
3769       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3770       nextNod[ iNode ] = *itNN[ iNode ]++;
3771     }
3772
3773     SMDS_MeshElement* aNewElem = 0;
3774     /*if(!elem->IsPoly())*/ {
3775       switch ( baseType ) {
3776       case SMDSEntity_0D:
3777       case SMDSEntity_Node: { // sweep NODE
3778         if ( nbSame == 0 ) {
3779           if ( isSingleNode[0] )
3780             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3781           else
3782             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3783         }
3784         else
3785           return;
3786         break;
3787       }
3788       case SMDSEntity_Edge: { // sweep EDGE
3789         if ( nbDouble == 0 )
3790         {
3791           if ( nbSame == 0 ) // ---> quadrangle
3792             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3793                                       nextNod[ 1 ], nextNod[ 0 ] );
3794           else               // ---> triangle
3795             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3796                                       nextNod[ iNotSameNode ] );
3797         }
3798         else                 // ---> polygon
3799         {
3800           vector<const SMDS_MeshNode*> poly_nodes;
3801           poly_nodes.push_back( prevNod[0] );
3802           poly_nodes.push_back( prevNod[1] );
3803           if ( prevNod[1] != nextNod[1] )
3804           {
3805             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3806             poly_nodes.push_back( nextNod[1] );
3807           }
3808           if ( prevNod[0] != nextNod[0] )
3809           {
3810             poly_nodes.push_back( nextNod[0] );
3811             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3812           }
3813           switch ( poly_nodes.size() ) {
3814           case 3:
3815             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3816             break;
3817           case 4:
3818             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3819                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3820             break;
3821           default:
3822             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3823           }
3824         }
3825         break;
3826       }
3827       case SMDSEntity_Triangle: // TRIANGLE --->
3828         {
3829           if ( nbDouble > 0 ) break;
3830           if ( nbSame == 0 )       // ---> pentahedron
3831             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3832                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3833
3834           else if ( nbSame == 1 )  // ---> pyramid
3835             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3836                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3837                                          nextNod[ iSameNode ]);
3838
3839           else // 2 same nodes:       ---> tetrahedron
3840             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3841                                          nextNod[ iNotSameNode ]);
3842           break;
3843         }
3844       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3845         {
3846           if ( nbSame == 2 )
3847             return;
3848           if ( nbDouble+nbSame == 2 )
3849           {
3850             if(nbSame==0) {      // ---> quadratic quadrangle
3851               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3852                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3853             }
3854             else { //(nbSame==1) // ---> quadratic triangle
3855               if(sames[0]==2) {
3856                 return; // medium node on axis
3857               }
3858               else if(sames[0]==0)
3859                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3860                                           nextNod[2], midlNod[1], prevNod[2]);
3861               else // sames[0]==1
3862                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3863                                           midlNod[0], nextNod[2], prevNod[2]);
3864             }
3865           }
3866           else if ( nbDouble == 3 )
3867           {
3868             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3869               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3870                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3871             }
3872           }
3873           else
3874             return;
3875           break;
3876         }
3877       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3878         if ( nbDouble > 0 ) break;
3879
3880         if ( nbSame == 0 )       // ---> hexahedron
3881           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3882                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3883
3884         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3885           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3886                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3887                                        nextNod[ iSameNode ]);
3888           newElems.push_back( aNewElem );
3889           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3890                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3891                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3892         }
3893         else if ( nbSame == 2 ) { // ---> pentahedron
3894           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3895             // iBeforeSame is same too
3896             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3897                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3898                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3899           else
3900             // iAfterSame is same too
3901             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3902                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3903                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3904         }
3905         break;
3906       }
3907       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3908         if ( nbDouble+nbSame != 3 ) break;
3909         if(nbSame==0) {
3910           // --->  pentahedron with 15 nodes
3911           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3912                                        nextNod[0], nextNod[1], nextNod[2],
3913                                        prevNod[3], prevNod[4], prevNod[5],
3914                                        nextNod[3], nextNod[4], nextNod[5],
3915                                        midlNod[0], midlNod[1], midlNod[2]);
3916         }
3917         else if(nbSame==1) {
3918           // --->  2d order pyramid of 13 nodes
3919           int apex = iSameNode;
3920           int i0 = ( apex + 1 ) % nbCorners;
3921           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3922           int i0a = apex + 3;
3923           int i1a = i1 + 3;
3924           int i01 = i0 + 3;
3925           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3926                                       nextNod[i0], nextNod[i1], prevNod[apex],
3927                                       prevNod[i01], midlNod[i0],
3928                                       nextNod[i01], midlNod[i1],
3929                                       prevNod[i1a], prevNod[i0a],
3930                                       nextNod[i0a], nextNod[i1a]);
3931         }
3932         else if(nbSame==2) {
3933           // --->  2d order tetrahedron of 10 nodes
3934           int n1 = iNotSameNode;
3935           int n2 = ( n1 + 1             ) % nbCorners;
3936           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3937           int n12 = n1 + 3;
3938           int n23 = n2 + 3;
3939           int n31 = n3 + 3;
3940           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3941                                        prevNod[n12], prevNod[n23], prevNod[n31],
3942                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3943         }
3944         break;
3945       }
3946       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3947         if( nbSame == 0 ) {
3948           if ( nbDouble != 4 ) break;
3949           // --->  hexahedron with 20 nodes
3950           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3951                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3952                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3953                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3954                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3955         }
3956         else if(nbSame==1) {
3957           // ---> pyramid + pentahedron - can not be created since it is needed
3958           // additional middle node at the center of face
3959           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3960           return;
3961         }
3962         else if( nbSame == 2 ) {
3963           if ( nbDouble != 2 ) break;
3964           // --->  2d order Pentahedron with 15 nodes
3965           int n1,n2,n4,n5;
3966           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3967             // iBeforeSame is same too
3968             n1 = iBeforeSame;
3969             n2 = iOpposSame;
3970             n4 = iSameNode;
3971             n5 = iAfterSame;
3972           }
3973           else {
3974             // iAfterSame is same too
3975             n1 = iSameNode;
3976             n2 = iBeforeSame;
3977             n4 = iAfterSame;
3978             n5 = iOpposSame;
3979           }
3980           int n12 = n2 + 4;
3981           int n45 = n4 + 4;
3982           int n14 = n1 + 4;
3983           int n25 = n5 + 4;
3984           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3985                                        prevNod[n4], prevNod[n5], nextNod[n5],
3986                                        prevNod[n12], midlNod[n2], nextNod[n12],
3987                                        prevNod[n45], midlNod[n5], nextNod[n45],
3988                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3989         }
3990         break;
3991       }
3992       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3993
3994         if( nbSame == 0 && nbDouble == 9 ) {
3995           // --->  tri-quadratic hexahedron with 27 nodes
3996           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3997                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3998                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3999                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4000                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4001                                        prevNod[8], // bottom center
4002                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4003                                        nextNod[8], // top center
4004                                        midlNod[8]);// elem center
4005         }
4006         else
4007         {
4008           return;
4009         }
4010         break;
4011       }
4012       case SMDSEntity_Polygon: { // sweep POLYGON
4013
4014         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4015           // --->  hexagonal prism
4016           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4017                                        prevNod[3], prevNod[4], prevNod[5],
4018                                        nextNod[0], nextNod[1], nextNod[2],
4019                                        nextNod[3], nextNod[4], nextNod[5]);
4020         }
4021         break;
4022       }
4023       case SMDSEntity_Ball:
4024         return;
4025
4026       default:
4027         break;
4028       }
4029     }
4030
4031     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4032     {
4033       if ( baseType != SMDSEntity_Polygon )
4034       {
4035         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4036         SMDS_MeshCell::applyInterlace( ind, prevNod );
4037         SMDS_MeshCell::applyInterlace( ind, nextNod );
4038         SMDS_MeshCell::applyInterlace( ind, midlNod );
4039         SMDS_MeshCell::applyInterlace( ind, itNN );
4040         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4041         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4042       }
4043       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4044       vector<int> quantities (nbNodes + 2);
4045       polyedre_nodes.clear();
4046       quantities.clear();
4047
4048       // bottom of prism
4049       for (int inode = 0; inode < nbNodes; inode++)
4050         polyedre_nodes.push_back( prevNod[inode] );
4051       quantities.push_back( nbNodes );
4052
4053       // top of prism
4054       polyedre_nodes.push_back( nextNod[0] );
4055       for (int inode = nbNodes; inode-1; --inode )
4056         polyedre_nodes.push_back( nextNod[inode-1] );
4057       quantities.push_back( nbNodes );
4058
4059       // side faces
4060       for (int iface = 0; iface < nbNodes; iface++)
4061       {
4062         const int prevNbNodes = polyedre_nodes.size();
4063         int inextface = (iface+1) % nbNodes;
4064         polyedre_nodes.push_back( prevNod[inextface] );
4065         polyedre_nodes.push_back( prevNod[iface] );
4066         if ( prevNod[iface] != nextNod[iface] )
4067         {
4068           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4069           polyedre_nodes.push_back( nextNod[iface] );
4070         }
4071         if ( prevNod[inextface] != nextNod[inextface] )
4072         {
4073           polyedre_nodes.push_back( nextNod[inextface] );
4074           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4075         }
4076         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4077         if ( nbFaceNodes > 2 )
4078           quantities.push_back( nbFaceNodes );
4079         else // degenerated face
4080           polyedre_nodes.resize( prevNbNodes );
4081       }
4082       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4083     }
4084
4085     if ( aNewElem ) {
4086       newElems.push_back( aNewElem );
4087       myLastCreatedElems.Append(aNewElem);
4088       srcElements.Append( elem );
4089     }
4090
4091     // set new prev nodes
4092     for ( iNode = 0; iNode < nbNodes; iNode++ )
4093       prevNod[ iNode ] = nextNod[ iNode ];
4094
4095   } // for steps
4096 }
4097
4098 //=======================================================================
4099 /*!
4100  * \brief Create 1D and 2D elements around swept elements
4101  * \param mapNewNodes - source nodes and ones generated from them
4102  * \param newElemsMap - source elements and ones generated from them
4103  * \param elemNewNodesMap - nodes generated from each node of each element
4104  * \param elemSet - all swept elements
4105  * \param nbSteps - number of sweeping steps
4106  * \param srcElements - to append elem for each generated element
4107  */
4108 //=======================================================================
4109
4110 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4111                                   TElemOfElemListMap &     newElemsMap,
4112                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4113                                   TIDSortedElemSet&        elemSet,
4114                                   const int                nbSteps,
4115                                   SMESH_SequenceOfElemPtr& srcElements)
4116 {
4117   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4118   SMESHDS_Mesh* aMesh = GetMeshDS();
4119
4120   // Find nodes belonging to only one initial element - sweep them to get edges.
4121
4122   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4123   for ( ; nList != mapNewNodes.end(); nList++ )
4124   {
4125     const SMDS_MeshNode* node =
4126       static_cast<const SMDS_MeshNode*>( nList->first );
4127     if ( newElemsMap.count( node ))
4128       continue; // node was extruded into edge
4129     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4130     int nbInitElems = 0;
4131     const SMDS_MeshElement* el = 0;
4132     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4133     while ( eIt->more() && nbInitElems < 2 ) {
4134       el = eIt->next();
4135       SMDSAbs_ElementType type = el->GetType();
4136       if ( type == SMDSAbs_Volume || type < highType ) continue;
4137       if ( type > highType ) {
4138         nbInitElems = 0;
4139         highType = type;
4140       }
4141       nbInitElems += elemSet.count(el);
4142     }
4143     if ( nbInitElems < 2 ) {
4144       bool NotCreateEdge = el && el->IsMediumNode(node);
4145       if(!NotCreateEdge) {
4146         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4147         list<const SMDS_MeshElement*> newEdges;
4148         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4149       }
4150     }
4151   }
4152
4153   // Make a ceiling for each element ie an equal element of last new nodes.
4154   // Find free links of faces - make edges and sweep them into faces.
4155
4156   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4157   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4158   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4159   {
4160     const SMDS_MeshElement* elem = itElem->first;
4161     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4162
4163     if(itElem->second.size()==0) continue;
4164
4165     const bool isQuadratic = elem->IsQuadratic();
4166
4167     if ( elem->GetType() == SMDSAbs_Edge ) {
4168       // create a ceiling edge
4169       if ( !isQuadratic ) {
4170         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4171                                vecNewNodes[ 1 ]->second.back())) {
4172           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4173                                                    vecNewNodes[ 1 ]->second.back()));
4174           srcElements.Append( elem );
4175         }
4176       }
4177       else {
4178         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4179                                vecNewNodes[ 1 ]->second.back(),
4180                                vecNewNodes[ 2 ]->second.back())) {
4181           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4182                                                    vecNewNodes[ 1 ]->second.back(),
4183                                                    vecNewNodes[ 2 ]->second.back()));
4184           srcElements.Append( elem );
4185         }
4186       }
4187     }
4188     if ( elem->GetType() != SMDSAbs_Face )
4189       continue;
4190
4191     bool hasFreeLinks = false;
4192
4193     TIDSortedElemSet avoidSet;
4194     avoidSet.insert( elem );
4195
4196     set<const SMDS_MeshNode*> aFaceLastNodes;
4197     int iNode, nbNodes = vecNewNodes.size();
4198     if ( !isQuadratic ) {
4199       // loop on the face nodes
4200       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4202         // look for free links of the face
4203         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4204         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4205         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4206         // check if a link n1-n2 is free
4207         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4208           hasFreeLinks = true;
4209           // make a new edge and a ceiling for a new edge
4210           const SMDS_MeshElement* edge;
4211           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4212             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4213             srcElements.Append( myLastCreatedElems.Last() );
4214           }
4215           n1 = vecNewNodes[ iNode ]->second.back();
4216           n2 = vecNewNodes[ iNext ]->second.back();
4217           if ( !aMesh->FindEdge( n1, n2 )) {
4218             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4219             srcElements.Append( edge );
4220           }
4221         }
4222       }
4223     }
4224     else { // elem is quadratic face
4225       int nbn = nbNodes/2;
4226       for ( iNode = 0; iNode < nbn; iNode++ ) {
4227         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4228         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4229         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4230         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4231         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4232         // check if a link is free
4233         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4234              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4235              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4236           hasFreeLinks = true;
4237           // make an edge and a ceiling for a new edge
4238           // find medium node
4239           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4240             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4241             srcElements.Append( elem );
4242           }
4243           n1 = vecNewNodes[ iNode ]->second.back();
4244           n2 = vecNewNodes[ iNext ]->second.back();
4245           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4246           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4247             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4248             srcElements.Append( elem );
4249           }
4250         }
4251       }
4252       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4253         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4254       }
4255     }
4256
4257     // sweep free links into faces
4258
4259     if ( hasFreeLinks )  {
4260       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4261       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4262
4263       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4264       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4265         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4266         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4267       }
4268       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4269         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4270         std::advance( v, volNb );
4271         // find indices of free faces of a volume and their source edges
4272         list< int > freeInd;
4273         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4274         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4275         int iF, nbF = vTool.NbFaces();
4276         for ( iF = 0; iF < nbF; iF ++ ) {
4277           if (vTool.IsFreeFace( iF ) &&
4278               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4279               initNodeSet != faceNodeSet) // except an initial face
4280           {
4281             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4282               continue;
4283             freeInd.push_back( iF );
4284             // find source edge of a free face iF
4285             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4286             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4287             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4288                                    initNodeSet.begin(), initNodeSet.end(),
4289                                    commonNodes.begin());
4290             if ( (*v)->IsQuadratic() )
4291               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4292             else
4293               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4294 #ifdef _DEBUG_
4295             if ( !srcEdges.back() )
4296             {
4297               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4298                    << iF << " of volume #" << vTool.ID() << endl;
4299             }
4300 #endif
4301           }
4302         }
4303         if ( freeInd.empty() )
4304           continue;
4305
4306         // create faces for all steps;
4307         // if such a face has been already created by sweep of edge,
4308         // assure that its orientation is OK
4309         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4310           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4311           vTool.SetExternalNormal();
4312           const int nextShift = vTool.IsForward() ? +1 : -1;
4313           list< int >::iterator ind = freeInd.begin();
4314           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4315           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4316           {
4317             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4318             int nbn = vTool.NbFaceNodes( *ind );
4319             const SMDS_MeshElement * f = 0;
4320             if ( nbn == 3 )              ///// triangle
4321             {
4322               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4323               if ( !f ||
4324                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4325               {
4326                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4327                                                      nodes[ 1 ],
4328                                                      nodes[ 1 + nextShift ] };
4329                 if ( f )
4330                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4331                 else
4332                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4333                                                             newOrder[ 2 ] ));
4334               }
4335             }
4336             else if ( nbn == 4 )       ///// quadrangle
4337             {
4338               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4339               if ( !f ||
4340                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4341               {
4342                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4343                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4344                 if ( f )
4345                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4346                 else
4347                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4348                                                             newOrder[ 2 ], newOrder[ 3 ]));
4349               }
4350             }
4351             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4352             {
4353               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4354               if ( !f ||
4355                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4356               {
4357                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4358                                                      nodes[2],
4359                                                      nodes[2 + 2*nextShift],
4360                                                      nodes[3 - 2*nextShift],
4361                                                      nodes[3],
4362                                                      nodes[3 + 2*nextShift]};
4363                 if ( f )
4364                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4365                 else
4366                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4367                                                             newOrder[ 1 ],
4368                                                             newOrder[ 2 ],
4369                                                             newOrder[ 3 ],
4370                                                             newOrder[ 4 ],
4371                                                             newOrder[ 5 ] ));
4372               }
4373             }
4374             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4375             {
4376               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4377                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4378               if ( !f ||
4379                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4380               {
4381                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4382                                                      nodes[4 - 2*nextShift],
4383                                                      nodes[4],
4384                                                      nodes[4 + 2*nextShift],
4385                                                      nodes[1],
4386                                                      nodes[5 - 2*nextShift],
4387                                                      nodes[5],
4388                                                      nodes[5 + 2*nextShift] };
4389                 if ( f )
4390                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4391                 else
4392                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4393                                                            newOrder[ 2 ], newOrder[ 3 ],
4394                                                            newOrder[ 4 ], newOrder[ 5 ],
4395                                                            newOrder[ 6 ], newOrder[ 7 ]));
4396               }
4397             }
4398             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4399             {
4400               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4401                                       SMDSAbs_Face, /*noMedium=*/false);
4402               if ( !f ||
4403                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4404               {
4405                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4406                                                      nodes[4 - 2*nextShift],
4407                                                      nodes[4],
4408                                                      nodes[4 + 2*nextShift],
4409                                                      nodes[1],
4410                                                      nodes[5 - 2*nextShift],
4411                                                      nodes[5],
4412                                                      nodes[5 + 2*nextShift],
4413                                                      nodes[8] };
4414                 if ( f )
4415                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4416                 else
4417                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4418                                                            newOrder[ 2 ], newOrder[ 3 ],
4419                                                            newOrder[ 4 ], newOrder[ 5 ],
4420                                                            newOrder[ 6 ], newOrder[ 7 ],
4421                                                            newOrder[ 8 ]));
4422               }
4423             }
4424             else  //////// polygon
4425             {
4426               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4427               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4428               if ( !f ||
4429                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4430               {
4431                 if ( !vTool.IsForward() )
4432                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4433                 if ( f )
4434                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4435                 else
4436                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4437               }
4438             }
4439
4440             while ( srcElements.Length() < myLastCreatedElems.Length() )
4441               srcElements.Append( *srcEdge );
4442
4443           }  // loop on free faces
4444
4445           // go to the next volume
4446           iVol = 0;
4447           while ( iVol++ < nbVolumesByStep ) v++;
4448
4449         } // loop on steps
4450       } // loop on volumes of one step
4451     } // sweep free links into faces
4452
4453     // Make a ceiling face with a normal external to a volume
4454
4455     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4456
4457     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4458     if ( iF >= 0 ) {
4459       lastVol.SetExternalNormal();
4460       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4461       int nbn = lastVol.NbFaceNodes( iF );
4462       if ( nbn == 3 ) {
4463         if (!hasFreeLinks ||
4464             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4465           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4466       }
4467       else if ( nbn == 4 )
4468       {
4469         if (!hasFreeLinks ||
4470             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4471           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4472       }
4473       else if ( nbn == 6 && isQuadratic )
4474       {
4475         if (!hasFreeLinks ||
4476             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4477           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4478                                                    nodes[1], nodes[3], nodes[5]));
4479       }
4480       else if ( nbn == 8 && isQuadratic )
4481       {
4482         if (!hasFreeLinks ||
4483             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4484                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4485           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4486                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4487       }
4488       else if ( nbn == 9 && isQuadratic )
4489       {
4490         if (!hasFreeLinks ||
4491             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4492                                 SMDSAbs_Face, /*noMedium=*/false) )
4493           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4494                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4495                                                    nodes[8]));
4496       }
4497       else {
4498         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4499         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4500           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4501       }
4502
4503       while ( srcElements.Length() < myLastCreatedElems.Length() )
4504         srcElements.Append( elem );
4505     }
4506   } // loop on swept elements
4507 }
4508
4509 //=======================================================================
4510 //function : RotationSweep
4511 //purpose  :
4512 //=======================================================================
4513
4514 SMESH_MeshEditor::PGroupIDs
4515 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4516                                 const gp_Ax1&      theAxis,
4517                                 const double       theAngle,
4518                                 const int          theNbSteps,
4519                                 const double       theTol,
4520                                 const bool         theMakeGroups,
4521                                 const bool         theMakeWalls)
4522 {
4523   myLastCreatedElems.Clear();
4524   myLastCreatedNodes.Clear();
4525
4526   // source elements for each generated one
4527   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4528
4529   MESSAGE( "RotationSweep()");
4530   gp_Trsf aTrsf;
4531   aTrsf.SetRotation( theAxis, theAngle );
4532   gp_Trsf aTrsf2;
4533   aTrsf2.SetRotation( theAxis, theAngle/2. );
4534
4535   gp_Lin aLine( theAxis );
4536   double aSqTol = theTol * theTol;
4537
4538   SMESHDS_Mesh* aMesh = GetMeshDS();
4539
4540   TNodeOfNodeListMap mapNewNodes;
4541   TElemOfVecOfNnlmiMap mapElemNewNodes;
4542   TElemOfElemListMap newElemsMap;
4543
4544   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4545                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4546                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4547   // loop on theElems
4548   TIDSortedElemSet::iterator itElem;
4549   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4550     const SMDS_MeshElement* elem = *itElem;
4551     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4552       continue;
4553     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4554     newNodesItVec.reserve( elem->NbNodes() );
4555
4556     // loop on elem nodes
4557     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4558     while ( itN->more() )
4559     {
4560       // check if a node has been already sweeped
4561       const SMDS_MeshNode* node = cast2Node( itN->next() );
4562
4563       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4564       double coord[3];
4565       aXYZ.Coord( coord[0], coord[1], coord[2] );
4566       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4567
4568       TNodeOfNodeListMapItr nIt =
4569         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4570       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4571       if ( listNewNodes.empty() )
4572       {
4573         // check if we are to create medium nodes between corner ones
4574         bool needMediumNodes = false;
4575         if ( isQuadraticMesh )
4576         {
4577           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4578           while (it->more() && !needMediumNodes )
4579           {
4580             const SMDS_MeshElement* invElem = it->next();
4581             if ( invElem != elem && !theElems.count( invElem )) continue;
4582             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4583             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4584               needMediumNodes = true;
4585           }
4586         }
4587
4588         // make new nodes
4589         const SMDS_MeshNode * newNode = node;
4590         for ( int i = 0; i < theNbSteps; i++ ) {
4591           if ( !isOnAxis ) {
4592             if ( needMediumNodes )  // create a medium node
4593             {
4594               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4595               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4596               myLastCreatedNodes.Append(newNode);
4597               srcNodes.Append( node );
4598               listNewNodes.push_back( newNode );
4599               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4600             }
4601             else {
4602               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4603             }
4604             // create a corner node
4605             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4606             myLastCreatedNodes.Append(newNode);
4607             srcNodes.Append( node );
4608             listNewNodes.push_back( newNode );
4609           }
4610           else {
4611             listNewNodes.push_back( newNode );
4612             // if ( needMediumNodes )
4613             //   listNewNodes.push_back( newNode );
4614           }
4615         }
4616       }
4617       newNodesItVec.push_back( nIt );
4618     }
4619     // make new elements
4620     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4621   }
4622
4623   if ( theMakeWalls )
4624     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4625
4626   PGroupIDs newGroupIDs;
4627   if ( theMakeGroups )
4628     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4629
4630   return newGroupIDs;
4631 }
4632
4633
4634 //=======================================================================
4635 //function : CreateNode
4636 //purpose  :
4637 //=======================================================================
4638 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4639                                                   const double y,
4640                                                   const double z,
4641                                                   const double tolnode,
4642                                                   SMESH_SequenceOfNode& aNodes)
4643 {
4644   // myLastCreatedElems.Clear();
4645   // myLastCreatedNodes.Clear();
4646
4647   gp_Pnt P1(x,y,z);
4648   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4649
4650   // try to search in sequence of existing nodes
4651   // if aNodes.Length()>0 we 'nave to use given sequence
4652   // else - use all nodes of mesh
4653   if(aNodes.Length()>0) {
4654     int i;
4655     for(i=1; i<=aNodes.Length(); i++) {
4656       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4657       if(P1.Distance(P2)<tolnode)
4658         return aNodes.Value(i);
4659     }
4660   }
4661   else {
4662     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4663     while(itn->more()) {
4664       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4665       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4666       if(P1.Distance(P2)<tolnode)
4667         return aN;
4668     }
4669   }
4670
4671   // create new node and return it
4672   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4673   //myLastCreatedNodes.Append(NewNode);
4674   return NewNode;
4675 }
4676
4677
4678 //=======================================================================
4679 //function : ExtrusionSweep
4680 //purpose  :
4681 //=======================================================================
4682
4683 SMESH_MeshEditor::PGroupIDs
4684 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4685                                   const gp_Vec&       theStep,
4686                                   const int           theNbSteps,
4687                                   TElemOfElemListMap& newElemsMap,
4688                                   const bool          theMakeGroups,
4689                                   const int           theFlags,
4690                                   const double        theTolerance)
4691 {
4692   ExtrusParam aParams;
4693   aParams.myDir = gp_Dir(theStep);
4694   aParams.myNodes.Clear();
4695   aParams.mySteps = new TColStd_HSequenceOfReal;
4696   int i;
4697   for(i=1; i<=theNbSteps; i++)
4698     aParams.mySteps->Append(theStep.Magnitude());
4699
4700   return
4701     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4702 }
4703
4704
4705 //=======================================================================
4706 //function : ExtrusionSweep
4707 //purpose  :
4708 //=======================================================================
4709
4710 SMESH_MeshEditor::PGroupIDs
4711 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4712                                   ExtrusParam&        theParams,
4713                                   TElemOfElemListMap& newElemsMap,
4714                                   const bool          theMakeGroups,
4715                                   const int           theFlags,
4716                                   const double        theTolerance)
4717 {
4718   myLastCreatedElems.Clear();
4719   myLastCreatedNodes.Clear();
4720
4721   // source elements for each generated one
4722   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4723
4724   SMESHDS_Mesh* aMesh = GetMeshDS();
4725
4726   int nbsteps = theParams.mySteps->Length();
4727
4728   TNodeOfNodeListMap mapNewNodes;
4729   //TNodeOfNodeVecMap mapNewNodes;
4730   TElemOfVecOfNnlmiMap mapElemNewNodes;
4731   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4732
4733   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4734                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4735                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4736   // loop on theElems
4737   TIDSortedElemSet::iterator itElem;
4738   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4739     // check element type
4740     const SMDS_MeshElement* elem = *itElem;
4741     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4742       continue;
4743
4744     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4745     newNodesItVec.reserve( elem->NbNodes() );
4746
4747     // loop on elem nodes
4748     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4749     while ( itN->more() )
4750     {
4751       // check if a node has been already sweeped
4752       const SMDS_MeshNode* node = cast2Node( itN->next() );
4753       TNodeOfNodeListMap::iterator nIt =
4754         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4755       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4756       if ( listNewNodes.empty() )
4757       {
4758         // make new nodes
4759
4760         // check if we are to create medium nodes between corner ones
4761         bool needMediumNodes = false;
4762         if ( isQuadraticMesh )
4763         {
4764           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4765           while (it->more() && !needMediumNodes )
4766           {
4767             const SMDS_MeshElement* invElem = it->next();
4768             if ( invElem != elem && !theElems.count( invElem )) continue;
4769             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4770             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4771               needMediumNodes = true;
4772           }
4773         }
4774
4775         double coord[] = { node->X(), node->Y(), node->Z() };
4776         for ( int i = 0; i < nbsteps; i++ )
4777         {
4778           if ( needMediumNodes ) // create a medium node
4779           {
4780             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4781             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4782             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4783             if( theFlags & EXTRUSION_FLAG_SEW ) {
4784               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4785                                                          theTolerance, theParams.myNodes);
4786               listNewNodes.push_back( newNode );
4787             }
4788             else {
4789               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4790               myLastCreatedNodes.Append(newNode);
4791               srcNodes.Append( node );
4792               listNewNodes.push_back( newNode );
4793             }
4794           }
4795           // create a corner node
4796           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4797           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4798           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4799           if( theFlags & EXTRUSION_FLAG_SEW ) {
4800             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4801                                                        theTolerance, theParams.myNodes);
4802             listNewNodes.push_back( newNode );
4803           }
4804           else {
4805             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4806             myLastCreatedNodes.Append(newNode);
4807             srcNodes.Append( node );
4808             listNewNodes.push_back( newNode );
4809           }
4810         }
4811       }
4812       newNodesItVec.push_back( nIt );
4813     }
4814     // make new elements
4815     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4816   }
4817
4818   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4819     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4820   }
4821   PGroupIDs newGroupIDs;
4822   if ( theMakeGroups )
4823     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4824
4825   return newGroupIDs;
4826 }
4827
4828 //=======================================================================
4829 //function : ExtrusionAlongTrack
4830 //purpose  :
4831 //=======================================================================
4832 SMESH_MeshEditor::Extrusion_Error
4833 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4834                                        SMESH_subMesh*       theTrack,
4835                                        const SMDS_MeshNode* theN1,
4836                                        const bool           theHasAngles,
4837                                        list<double>&        theAngles,
4838                                        const bool           theLinearVariation,
4839                                        const bool           theHasRefPoint,
4840                                        const gp_Pnt&        theRefPoint,
4841                                        const bool           theMakeGroups)
4842 {
4843   MESSAGE("ExtrusionAlongTrack");
4844   myLastCreatedElems.Clear();
4845   myLastCreatedNodes.Clear();
4846
4847   int aNbE;
4848   std::list<double> aPrms;
4849   TIDSortedElemSet::iterator itElem;
4850
4851   gp_XYZ aGC;
4852   TopoDS_Edge aTrackEdge;
4853   TopoDS_Vertex aV1, aV2;
4854
4855   SMDS_ElemIteratorPtr aItE;
4856   SMDS_NodeIteratorPtr aItN;
4857   SMDSAbs_ElementType aTypeE;
4858
4859   TNodeOfNodeListMap mapNewNodes;
4860
4861   // 1. Check data
4862   aNbE = theElements.size();
4863   // nothing to do
4864   if ( !aNbE )
4865     return EXTR_NO_ELEMENTS;
4866
4867   // 1.1 Track Pattern
4868   ASSERT( theTrack );
4869
4870   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4871
4872   aItE = pSubMeshDS->GetElements();
4873   while ( aItE->more() ) {
4874     const SMDS_MeshElement* pE = aItE->next();
4875     aTypeE = pE->GetType();
4876     // Pattern must contain links only
4877     if ( aTypeE != SMDSAbs_Edge )
4878       return EXTR_PATH_NOT_EDGE;
4879   }
4880
4881   list<SMESH_MeshEditor_PathPoint> fullList;
4882
4883   const TopoDS_Shape& aS = theTrack->GetSubShape();
4884   // Sub-shape for the Pattern must be an Edge or Wire
4885   if( aS.ShapeType() == TopAbs_EDGE ) {
4886     aTrackEdge = TopoDS::Edge( aS );
4887     // the Edge must not be degenerated
4888     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4889       return EXTR_BAD_PATH_SHAPE;
4890     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4891     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4892     const SMDS_MeshNode* aN1 = aItN->next();
4893     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4894     const SMDS_MeshNode* aN2 = aItN->next();
4895     // starting node must be aN1 or aN2
4896     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4897       return EXTR_BAD_STARTING_NODE;
4898     aItN = pSubMeshDS->GetNodes();
4899     while ( aItN->more() ) {
4900       const SMDS_MeshNode* pNode = aItN->next();
4901       const SMDS_EdgePosition* pEPos =
4902         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4903       double aT = pEPos->GetUParameter();
4904       aPrms.push_back( aT );
4905     }
4906     //Extrusion_Error err =
4907     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4908   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4909     list< SMESH_subMesh* > LSM;
4910     TopTools_SequenceOfShape Edges;
4911     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4912     while(itSM->more()) {
4913       SMESH_subMesh* SM = itSM->next();
4914       LSM.push_back(SM);
4915       const TopoDS_Shape& aS = SM->GetSubShape();
4916       Edges.Append(aS);
4917     }
4918     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4919     int startNid = theN1->GetID();
4920     TColStd_MapOfInteger UsedNums;
4921
4922     int NbEdges = Edges.Length();
4923     int i = 1;
4924     for(; i<=NbEdges; i++) {
4925       int k = 0;
4926       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4927       for(; itLSM!=LSM.end(); itLSM++) {
4928         k++;
4929         if(UsedNums.Contains(k)) continue;
4930         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4931         SMESH_subMesh* locTrack = *itLSM;
4932         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4933         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4934         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4935         const SMDS_MeshNode* aN1 = aItN->next();
4936         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4937         const SMDS_MeshNode* aN2 = aItN->next();
4938         // starting node must be aN1 or aN2
4939         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4940         // 2. Collect parameters on the track edge
4941         aPrms.clear();
4942         aItN = locMeshDS->GetNodes();
4943         while ( aItN->more() ) {
4944           const SMDS_MeshNode* pNode = aItN->next();
4945           const SMDS_EdgePosition* pEPos =
4946             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4947           double aT = pEPos->GetUParameter();
4948           aPrms.push_back( aT );
4949         }
4950         list<SMESH_MeshEditor_PathPoint> LPP;
4951         //Extrusion_Error err =
4952         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4953         LLPPs.push_back(LPP);
4954         UsedNums.Add(k);
4955         // update startN for search following egde
4956         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4957         else startNid = aN1->GetID();
4958         break;
4959       }
4960     }
4961     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4962     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4963     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4964     for(; itPP!=firstList.end(); itPP++) {
4965       fullList.push_back( *itPP );
4966     }
4967     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4968     fullList.pop_back();
4969     itLLPP++;
4970     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4971       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4972       itPP = currList.begin();
4973       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4974       gp_Dir D1 = PP1.Tangent();
4975       gp_Dir D2 = PP2.Tangent();
4976       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4977                            (D1.Z()+D2.Z())/2 ) );
4978       PP1.SetTangent(Dnew);
4979       fullList.push_back(PP1);
4980       itPP++;
4981       for(; itPP!=firstList.end(); itPP++) {
4982         fullList.push_back( *itPP );
4983       }
4984       PP1 = fullList.back();
4985       fullList.pop_back();
4986     }
4987     // if wire not closed
4988     fullList.push_back(PP1);
4989     // else ???
4990   }
4991   else {
4992     return EXTR_BAD_PATH_SHAPE;
4993   }
4994
4995   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4996                           theHasRefPoint, theRefPoint, theMakeGroups);
4997 }
4998
4999
5000 //=======================================================================
5001 //function : ExtrusionAlongTrack
5002 //purpose  :
5003 //=======================================================================
5004 SMESH_MeshEditor::Extrusion_Error
5005 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5006                                        SMESH_Mesh*          theTrack,
5007                                        const SMDS_MeshNode* theN1,
5008                                        const bool           theHasAngles,
5009                                        list<double>&        theAngles,
5010                                        const bool           theLinearVariation,
5011                                        const bool           theHasRefPoint,
5012                                        const gp_Pnt&        theRefPoint,
5013                                        const bool           theMakeGroups)
5014 {
5015   myLastCreatedElems.Clear();
5016   myLastCreatedNodes.Clear();
5017
5018   int aNbE;
5019   std::list<double> aPrms;
5020   TIDSortedElemSet::iterator itElem;
5021
5022   gp_XYZ aGC;
5023   TopoDS_Edge aTrackEdge;
5024   TopoDS_Vertex aV1, aV2;
5025
5026   SMDS_ElemIteratorPtr aItE;
5027   SMDS_NodeIteratorPtr aItN;
5028   SMDSAbs_ElementType aTypeE;
5029
5030   TNodeOfNodeListMap mapNewNodes;
5031
5032   // 1. Check data
5033   aNbE = theElements.size();
5034   // nothing to do
5035   if ( !aNbE )
5036     return EXTR_NO_ELEMENTS;
5037
5038   // 1.1 Track Pattern
5039   ASSERT( theTrack );
5040
5041   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5042
5043   aItE = pMeshDS->elementsIterator();
5044   while ( aItE->more() ) {
5045     const SMDS_MeshElement* pE = aItE->next();
5046     aTypeE = pE->GetType();
5047     // Pattern must contain links only
5048     if ( aTypeE != SMDSAbs_Edge )
5049       return EXTR_PATH_NOT_EDGE;
5050   }
5051
5052   list<SMESH_MeshEditor_PathPoint> fullList;
5053
5054   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5055
5056   if( aS == SMESH_Mesh::PseudoShape() ) {
5057     //Mesh without shape
5058     const SMDS_MeshNode* currentNode = NULL;
5059     const SMDS_MeshNode* prevNode = theN1;
5060     std::vector<const SMDS_MeshNode*> aNodesList;
5061     aNodesList.push_back(theN1);
5062     int nbEdges = 0, conn=0;
5063     const SMDS_MeshElement* prevElem = NULL;
5064     const SMDS_MeshElement* currentElem = NULL;
5065     int totalNbEdges = theTrack->NbEdges();
5066     SMDS_ElemIteratorPtr nIt;
5067
5068     //check start node
5069     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5070       return EXTR_BAD_STARTING_NODE;
5071     }
5072
5073     conn = nbEdgeConnectivity(theN1);
5074     if(conn > 2)
5075       return EXTR_PATH_NOT_EDGE;
5076
5077     aItE = theN1->GetInverseElementIterator();
5078     prevElem = aItE->next();
5079     currentElem = prevElem;
5080     //Get all nodes
5081     if(totalNbEdges == 1 ) {
5082       nIt = currentElem->nodesIterator();
5083       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5084       if(currentNode == prevNode)
5085         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5086       aNodesList.push_back(currentNode);
5087     } else {
5088       nIt = currentElem->nodesIterator();
5089       while( nIt->more() ) {
5090         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5091         if(currentNode == prevNode)
5092           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5093         aNodesList.push_back(currentNode);
5094
5095         //case of the closed mesh
5096         if(currentNode == theN1) {
5097           nbEdges++;
5098           break;
5099         }
5100
5101         conn = nbEdgeConnectivity(currentNode);
5102         if(conn > 2) {
5103           return EXTR_PATH_NOT_EDGE;
5104         }else if( conn == 1 && nbEdges > 0 ) {
5105           //End of the path
5106           nbEdges++;
5107           break;
5108         }else {
5109           prevNode = currentNode;
5110           aItE = currentNode->GetInverseElementIterator();
5111           currentElem = aItE->next();
5112           if( currentElem  == prevElem)
5113             currentElem = aItE->next();
5114           nIt = currentElem->nodesIterator();
5115           prevElem = currentElem;
5116           nbEdges++;
5117         }
5118       }
5119     }
5120
5121     if(nbEdges != totalNbEdges)
5122       return EXTR_PATH_NOT_EDGE;
5123
5124     TopTools_SequenceOfShape Edges;
5125     double x1,x2,y1,y2,z1,z2;
5126     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5127     int startNid = theN1->GetID();
5128     for(int i = 1; i < aNodesList.size(); i++) {
5129       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5130       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5131       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5132       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5133       list<SMESH_MeshEditor_PathPoint> LPP;
5134       aPrms.clear();
5135       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5136       LLPPs.push_back(LPP);
5137       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5138       else startNid = aNodesList[i-1]->GetID();
5139
5140     }
5141
5142     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5143     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5144     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5145     for(; itPP!=firstList.end(); itPP++) {
5146       fullList.push_back( *itPP );
5147     }
5148
5149     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5150     SMESH_MeshEditor_PathPoint PP2;
5151     fullList.pop_back();
5152     itLLPP++;
5153     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5154       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5155       itPP = currList.begin();
5156       PP2 = currList.front();
5157       gp_Dir D1 = PP1.Tangent();
5158       gp_Dir D2 = PP2.Tangent();
5159       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5160                            (D1.Z()+D2.Z())/2 ) );
5161       PP1.SetTangent(Dnew);
5162       fullList.push_back(PP1);
5163       itPP++;
5164       for(; itPP!=currList.end(); itPP++) {
5165         fullList.push_back( *itPP );
5166       }
5167       PP1 = fullList.back();
5168       fullList.pop_back();
5169     }
5170     fullList.push_back(PP1);
5171
5172   } // Sub-shape for the Pattern must be an Edge or Wire
5173   else if( aS.ShapeType() == TopAbs_EDGE ) {
5174     aTrackEdge = TopoDS::Edge( aS );
5175     // the Edge must not be degenerated
5176     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5177       return EXTR_BAD_PATH_SHAPE;
5178     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5179     const SMDS_MeshNode* aN1 = 0;
5180     const SMDS_MeshNode* aN2 = 0;
5181     if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5182       aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5183       aN1 = aItN->next();
5184     }
5185     if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5186       aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5187       aN2 = aItN->next();
5188     }
5189     // starting node must be aN1 or aN2
5190     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5191       return EXTR_BAD_STARTING_NODE;
5192     aItN = pMeshDS->nodesIterator();
5193     while ( aItN->more() ) {
5194       const SMDS_MeshNode* pNode = aItN->next();
5195       if( pNode==aN1 || pNode==aN2 ) continue;
5196       const SMDS_EdgePosition* pEPos =
5197         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5198       double aT = pEPos->GetUParameter();
5199       aPrms.push_back( aT );
5200     }
5201     //Extrusion_Error err =
5202     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5203   }
5204   else if( aS.ShapeType() == TopAbs_WIRE ) {
5205     list< SMESH_subMesh* > LSM;
5206     TopTools_SequenceOfShape Edges;
5207     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5208     for(; eExp.More(); eExp.Next()) {
5209       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5210       if( BRep_Tool::Degenerated(E) ) continue;
5211       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5212       if(SM) {
5213         LSM.push_back(SM);
5214         Edges.Append(E);
5215       }
5216     }
5217     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5218     TopoDS_Vertex aVprev;
5219     TColStd_MapOfInteger UsedNums;
5220     int NbEdges = Edges.Length();
5221     int i = 1;
5222     for(; i<=NbEdges; i++) {
5223       int k = 0;
5224       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5225       for(; itLSM!=LSM.end(); itLSM++) {
5226         k++;
5227         if(UsedNums.Contains(k)) continue;
5228         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5229         SMESH_subMesh* locTrack = *itLSM;
5230         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5231         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5232         bool aN1isOK = false, aN2isOK = false;
5233         if ( aVprev.IsNull() ) {
5234           // if previous vertex is not yet defined, it means that we in the beginning of wire
5235           // and we have to find initial vertex corresponding to starting node theN1
5236           const SMDS_MeshNode* aN1 = 0;
5237           const SMDS_MeshNode* aN2 = 0;
5238
5239           if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5240             aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5241             aN1 = aItN->next();
5242           }
5243           if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5244             aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5245             aN2 = aItN->next();
5246           }
5247           // starting node must be aN1 or aN2
5248           aN1isOK = ( aN1 && aN1 == theN1 );
5249           aN2isOK = ( aN2 && aN2 == theN1 );
5250         }
5251         else {
5252           // we have specified ending vertex of the previous edge on the previous iteration
5253           // and we have just to check that it corresponds to any vertex in current segment
5254           aN1isOK = aVprev.IsSame( aV1 );
5255           aN2isOK = aVprev.IsSame( aV2 );
5256         }
5257         if ( !aN1isOK && !aN2isOK ) continue;
5258         // 2. Collect parameters on the track edge
5259         aPrms.clear();
5260         aItN = locMeshDS->GetNodes();
5261         while ( aItN->more() ) {
5262           const SMDS_MeshNode*     pNode = aItN->next();
5263           const SMDS_EdgePosition* pEPos =
5264             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5265           double aT = pEPos->GetUParameter();
5266           aPrms.push_back( aT );
5267         }
5268         list<SMESH_MeshEditor_PathPoint> LPP;
5269         //Extrusion_Error err =
5270         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5271         LLPPs.push_back(LPP);
5272         UsedNums.Add(k);
5273         // update startN for search following egde
5274         if ( aN1isOK ) aVprev = aV2;
5275         else           aVprev = aV1;
5276         break;
5277       }
5278     }
5279     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5280     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5281     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5282     for(; itPP!=firstList.end(); itPP++) {
5283       fullList.push_back( *itPP );
5284     }
5285     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5286     fullList.pop_back();
5287     itLLPP++;
5288     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5289       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5290       itPP = currList.begin();
5291       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5292       gp_Dir D1 = PP1.Tangent();
5293       gp_Dir D2 = PP2.Tangent();
5294       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5295       PP1.SetTangent(Dnew);
5296       fullList.push_back(PP1);
5297       itPP++;
5298       for(; itPP!=currList.end(); itPP++) {
5299         fullList.push_back( *itPP );
5300       }
5301       PP1 = fullList.back();
5302       fullList.pop_back();
5303     }
5304     // if wire not closed
5305     fullList.push_back(PP1);
5306     // else ???
5307   }
5308   else {
5309     return EXTR_BAD_PATH_SHAPE;
5310   }
5311
5312   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5313                           theHasRefPoint, theRefPoint, theMakeGroups);
5314 }
5315
5316
5317 //=======================================================================
5318 //function : MakeEdgePathPoints
5319 //purpose  : auxilary for ExtrusionAlongTrack
5320 //=======================================================================
5321 SMESH_MeshEditor::Extrusion_Error
5322 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5323                                      const TopoDS_Edge& aTrackEdge,
5324                                      bool FirstIsStart,
5325                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5326 {
5327   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5328   aTolVec=1.e-7;
5329   aTolVec2=aTolVec*aTolVec;
5330   double aT1, aT2;
5331   TopoDS_Vertex aV1, aV2;
5332   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5333   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5334   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5335   // 2. Collect parameters on the track edge
5336   aPrms.push_front( aT1 );
5337   aPrms.push_back( aT2 );
5338   // sort parameters
5339   aPrms.sort();
5340   if( FirstIsStart ) {
5341     if ( aT1 > aT2 ) {
5342       aPrms.reverse();
5343     }
5344   }
5345   else {
5346     if ( aT2 > aT1 ) {
5347       aPrms.reverse();
5348     }
5349   }
5350   // 3. Path Points
5351   SMESH_MeshEditor_PathPoint aPP;
5352   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5353   std::list<double>::iterator aItD = aPrms.begin();
5354   for(; aItD != aPrms.end(); ++aItD) {
5355     double aT = *aItD;
5356     gp_Pnt aP3D;
5357     gp_Vec aVec;
5358     aC3D->D1( aT, aP3D, aVec );
5359     aL2 = aVec.SquareMagnitude();
5360     if ( aL2 < aTolVec2 )
5361       return EXTR_CANT_GET_TANGENT;
5362     gp_Dir aTgt( aVec );
5363     aPP.SetPnt( aP3D );
5364     aPP.SetTangent( aTgt );
5365     aPP.SetParameter( aT );
5366     LPP.push_back(aPP);
5367   }
5368   return EXTR_OK;
5369 }
5370
5371
5372 //=======================================================================
5373 //function : MakeExtrElements
5374 //purpose  : auxilary for ExtrusionAlongTrack
5375 //=======================================================================
5376 SMESH_MeshEditor::Extrusion_Error
5377 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5378                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5379                                    const bool theHasAngles,
5380                                    list<double>& theAngles,
5381                                    const bool theLinearVariation,
5382                                    const bool theHasRefPoint,
5383                                    const gp_Pnt& theRefPoint,
5384                                    const bool theMakeGroups)
5385 {
5386   MESSAGE("MakeExtrElements");
5387   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5388   int aNbTP = fullList.size();
5389   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5390   // Angles
5391   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5392     LinearAngleVariation(aNbTP-1, theAngles);
5393   }
5394   vector<double> aAngles( aNbTP );
5395   int j = 0;
5396   for(; j<aNbTP; ++j) {
5397     aAngles[j] = 0.;
5398   }
5399   if ( theHasAngles ) {
5400     double anAngle;;
5401     std::list<double>::iterator aItD = theAngles.begin();
5402     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5403       anAngle = *aItD;
5404       aAngles[j] = anAngle;
5405     }
5406   }
5407   // fill vector of path points with angles
5408   //aPPs.resize(fullList.size());
5409   j = -1;
5410   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5411   for(; itPP!=fullList.end(); itPP++) {
5412     j++;
5413     SMESH_MeshEditor_PathPoint PP = *itPP;
5414     PP.SetAngle(aAngles[j]);
5415     aPPs[j] = PP;
5416   }
5417
5418   TNodeOfNodeListMap mapNewNodes;
5419   TElemOfVecOfNnlmiMap mapElemNewNodes;
5420   TElemOfElemListMap newElemsMap;
5421   TIDSortedElemSet::iterator itElem;
5422   double aX, aY, aZ;
5423   int aNb;
5424   SMDSAbs_ElementType aTypeE;
5425   // source elements for each generated one
5426   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5427
5428   // 3. Center of rotation aV0
5429   gp_Pnt aV0 = theRefPoint;
5430   gp_XYZ aGC;
5431   if ( !theHasRefPoint ) {
5432     aNb = 0;
5433     aGC.SetCoord( 0.,0.,0. );
5434
5435     itElem = theElements.begin();
5436     for ( ; itElem != theElements.end(); itElem++ ) {
5437       const SMDS_MeshElement* elem = *itElem;
5438
5439       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5440       while ( itN->more() ) {
5441         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5442         aX = node->X();
5443         aY = node->Y();
5444         aZ = node->Z();
5445
5446         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5447           list<const SMDS_MeshNode*> aLNx;
5448           mapNewNodes[node] = aLNx;
5449           //
5450           gp_XYZ aXYZ( aX, aY, aZ );
5451           aGC += aXYZ;
5452           ++aNb;
5453         }
5454       }
5455     }
5456     aGC /= aNb;
5457     aV0.SetXYZ( aGC );
5458   } // if (!theHasRefPoint) {
5459   mapNewNodes.clear();
5460
5461   // 4. Processing the elements
5462   SMESHDS_Mesh* aMesh = GetMeshDS();
5463
5464   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5465     // check element type
5466     const SMDS_MeshElement* elem = *itElem;
5467     aTypeE = elem->GetType();
5468     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5469       continue;
5470
5471     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5472     newNodesItVec.reserve( elem->NbNodes() );
5473
5474     // loop on elem nodes
5475     int nodeIndex = -1;
5476     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5477     while ( itN->more() )
5478     {
5479       ++nodeIndex;
5480       // check if a node has been already processed
5481       const SMDS_MeshNode* node =
5482         static_cast<const SMDS_MeshNode*>( itN->next() );
5483       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5484       if ( nIt == mapNewNodes.end() ) {
5485         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5486         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5487
5488         // make new nodes
5489         aX = node->X();  aY = node->Y(); aZ = node->Z();
5490
5491         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5492         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5493         gp_Ax1 anAx1, anAxT1T0;
5494         gp_Dir aDT1x, aDT0x, aDT1T0;
5495
5496         aTolAng=1.e-4;
5497
5498         aV0x = aV0;
5499         aPN0.SetCoord(aX, aY, aZ);
5500
5501         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5502         aP0x = aPP0.Pnt();
5503         aDT0x= aPP0.Tangent();
5504         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5505
5506         for ( j = 1; j < aNbTP; ++j ) {
5507           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5508           aP1x = aPP1.Pnt();
5509           aDT1x = aPP1.Tangent();
5510           aAngle1x = aPP1.Angle();
5511
5512           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5513           // Translation
5514           gp_Vec aV01x( aP0x, aP1x );
5515           aTrsf.SetTranslation( aV01x );
5516
5517           // traslated point
5518           aV1x = aV0x.Transformed( aTrsf );
5519           aPN1 = aPN0.Transformed( aTrsf );
5520
5521           // rotation 1 [ T1,T0 ]
5522           aAngleT1T0=-aDT1x.Angle( aDT0x );
5523           if (fabs(aAngleT1T0) > aTolAng) {
5524             aDT1T0=aDT1x^aDT0x;
5525             anAxT1T0.SetLocation( aV1x );
5526             anAxT1T0.SetDirection( aDT1T0 );
5527             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5528
5529             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5530           }
5531
5532           // rotation 2
5533           if ( theHasAngles ) {
5534             anAx1.SetLocation( aV1x );
5535             anAx1.SetDirection( aDT1x );
5536             aTrsfRot.SetRotation( anAx1, aAngle1x );
5537
5538             aPN1 = aPN1.Transformed( aTrsfRot );
5539           }
5540
5541           // make new node
5542           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5543           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5544             // create additional node
5545             double x = ( aPN1.X() + aPN0.X() )/2.;
5546             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5547             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5548             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5549             myLastCreatedNodes.Append(newNode);
5550             srcNodes.Append( node );
5551             listNewNodes.push_back( newNode );
5552           }
5553           aX = aPN1.X();
5554           aY = aPN1.Y();
5555           aZ = aPN1.Z();
5556           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5557           myLastCreatedNodes.Append(newNode);
5558           srcNodes.Append( node );
5559           listNewNodes.push_back( newNode );
5560
5561           aPN0 = aPN1;
5562           aP0x = aP1x;
5563           aV0x = aV1x;
5564           aDT0x = aDT1x;
5565         }
5566       }
5567
5568       else {
5569         // if current elem is quadratic and current node is not medium
5570         // we have to check - may be it is needed to insert additional nodes
5571         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5572           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5573           if(listNewNodes.size()==aNbTP-1) {
5574             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5575             gp_XYZ P(node->X(), node->Y(), node->Z());
5576             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5577             int i;
5578             for(i=0; i<aNbTP-1; i++) {
5579               const SMDS_MeshNode* N = *it;
5580               double x = ( N->X() + P.X() )/2.;
5581               double y = ( N->Y() + P.Y() )/2.;
5582               double z = ( N->Z() + P.Z() )/2.;
5583               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5584               srcNodes.Append( node );
5585               myLastCreatedNodes.Append(newN);
5586               aNodes[2*i] = newN;
5587               aNodes[2*i+1] = N;
5588               P = gp_XYZ(N->X(),N->Y(),N->Z());
5589             }
5590             listNewNodes.clear();
5591             for(i=0; i<2*(aNbTP-1); i++) {
5592               listNewNodes.push_back(aNodes[i]);
5593             }
5594           }
5595         }
5596       }
5597
5598       newNodesItVec.push_back( nIt );
5599     }
5600     // make new elements
5601     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5602     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5603     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5604   }
5605
5606   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5607
5608   if ( theMakeGroups )
5609     generateGroups( srcNodes, srcElems, "extruded");
5610
5611   return EXTR_OK;
5612 }
5613
5614
5615 //=======================================================================
5616 //function : LinearAngleVariation
5617 //purpose  : auxilary for ExtrusionAlongTrack
5618 //=======================================================================
5619 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5620                                             list<double>& Angles)
5621 {
5622   int nbAngles = Angles.size();
5623   if( nbSteps > nbAngles ) {
5624     vector<double> theAngles(nbAngles);
5625     list<double>::iterator it = Angles.begin();
5626     int i = -1;
5627     for(; it!=Angles.end(); it++) {
5628       i++;
5629       theAngles[i] = (*it);
5630     }
5631     list<double> res;
5632     double rAn2St = double( nbAngles ) / double( nbSteps );
5633     double angPrev = 0, angle;
5634     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5635       double angCur = rAn2St * ( iSt+1 );
5636       double angCurFloor  = floor( angCur );
5637       double angPrevFloor = floor( angPrev );
5638       if ( angPrevFloor == angCurFloor )
5639         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5640       else {
5641         int iP = int( angPrevFloor );
5642         double angPrevCeil = ceil(angPrev);
5643         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5644
5645         int iC = int( angCurFloor );
5646         if ( iC < nbAngles )
5647           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5648
5649         iP = int( angPrevCeil );
5650         while ( iC-- > iP )
5651           angle += theAngles[ iC ];
5652       }
5653       res.push_back(angle);
5654       angPrev = angCur;
5655     }
5656     Angles.clear();
5657     it = res.begin();
5658     for(; it!=res.end(); it++)
5659       Angles.push_back( *it );
5660   }
5661 }
5662
5663
5664 //================================================================================
5665 /*!
5666  * \brief Move or copy theElements applying theTrsf to their nodes
5667  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5668  *  \param theTrsf - transformation to apply
5669  *  \param theCopy - if true, create translated copies of theElems
5670  *  \param theMakeGroups - if true and theCopy, create translated groups
5671  *  \param theTargetMesh - mesh to copy translated elements into
5672  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5673  */
5674 //================================================================================
5675
5676 SMESH_MeshEditor::PGroupIDs
5677 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5678                              const gp_Trsf&     theTrsf,
5679                              const bool         theCopy,
5680                              const bool         theMakeGroups,
5681                              SMESH_Mesh*        theTargetMesh)
5682 {
5683   myLastCreatedElems.Clear();
5684   myLastCreatedNodes.Clear();
5685
5686   bool needReverse = false;
5687   string groupPostfix;
5688   switch ( theTrsf.Form() ) {
5689   case gp_PntMirror:
5690     MESSAGE("gp_PntMirror");
5691     needReverse = true;
5692     groupPostfix = "mirrored";
5693     break;
5694   case gp_Ax1Mirror:
5695     MESSAGE("gp_Ax1Mirror");
5696     groupPostfix = "mirrored";
5697     break;
5698   case gp_Ax2Mirror:
5699     MESSAGE("gp_Ax2Mirror");
5700     needReverse = true;
5701     groupPostfix = "mirrored";
5702     break;
5703   case gp_Rotation:
5704     MESSAGE("gp_Rotation");
5705     groupPostfix = "rotated";
5706     break;
5707   case gp_Translation:
5708     MESSAGE("gp_Translation");
5709     groupPostfix = "translated";
5710     break;
5711   case gp_Scale:
5712     MESSAGE("gp_Scale");
5713     groupPostfix = "scaled";
5714     break;
5715   case gp_CompoundTrsf: // different scale by axis
5716     MESSAGE("gp_CompoundTrsf");
5717     groupPostfix = "scaled";
5718     break;
5719   default:
5720     MESSAGE("default");
5721     needReverse = false;
5722     groupPostfix = "transformed";
5723   }
5724
5725   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5726   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5727   SMESHDS_Mesh* aMesh    = GetMeshDS();
5728
5729
5730   // map old node to new one
5731   TNodeNodeMap nodeMap;
5732
5733   // elements sharing moved nodes; those of them which have all
5734   // nodes mirrored but are not in theElems are to be reversed
5735   TIDSortedElemSet inverseElemSet;
5736
5737   // source elements for each generated one
5738   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5739
5740   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5741   TIDSortedElemSet orphanNode;
5742
5743   if ( theElems.empty() ) // transform the whole mesh
5744   {
5745     // add all elements
5746     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5747     while ( eIt->more() ) theElems.insert( eIt->next() );
5748     // add orphan nodes
5749     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5750     while ( nIt->more() )
5751     {
5752       const SMDS_MeshNode* node = nIt->next();
5753       if ( node->NbInverseElements() == 0)
5754         orphanNode.insert( node );
5755     }
5756   }
5757
5758   // loop on elements to transform nodes : first orphan nodes then elems
5759   TIDSortedElemSet::iterator itElem;
5760   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5761   for (int i=0; i<2; i++)
5762   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5763     const SMDS_MeshElement* elem = *itElem;
5764     if ( !elem )
5765       continue;
5766
5767     // loop on elem nodes
5768     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5769     while ( itN->more() ) {
5770
5771       const SMDS_MeshNode* node = cast2Node( itN->next() );
5772       // check if a node has been already transformed
5773       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5774         nodeMap.insert( make_pair ( node, node ));
5775       if ( !n2n_isnew.second )
5776         continue;
5777
5778       double coord[3];
5779       coord[0] = node->X();
5780       coord[1] = node->Y();
5781       coord[2] = node->Z();
5782       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5783       if ( theTargetMesh ) {
5784         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5785         n2n_isnew.first->second = newNode;
5786         myLastCreatedNodes.Append(newNode);
5787         srcNodes.Append( node );
5788       }
5789       else if ( theCopy ) {
5790         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5791         n2n_isnew.first->second = newNode;
5792         myLastCreatedNodes.Append(newNode);
5793         srcNodes.Append( node );
5794       }
5795       else {
5796         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5797         // node position on shape becomes invalid
5798         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5799           ( SMDS_SpacePosition::originSpacePosition() );
5800       }
5801
5802       // keep inverse elements
5803       if ( !theCopy && !theTargetMesh && needReverse ) {
5804         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5805         while ( invElemIt->more() ) {
5806           const SMDS_MeshElement* iel = invElemIt->next();
5807           inverseElemSet.insert( iel );
5808         }
5809       }
5810     }
5811   }
5812
5813   // either create new elements or reverse mirrored ones
5814   if ( !theCopy && !needReverse && !theTargetMesh )
5815     return PGroupIDs();
5816
5817   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5818   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5819     theElems.insert( *invElemIt );
5820
5821   // Replicate or reverse elements
5822
5823   std::vector<int> iForw;
5824   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5825   {
5826     const SMDS_MeshElement* elem = *itElem;
5827     if ( !elem ) continue;
5828
5829     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5830     int                  nbNodes  = elem->NbNodes();
5831     if ( geomType == SMDSGeom_NONE ) continue; // node
5832
5833     switch ( geomType ) {
5834
5835     case SMDSGeom_POLYGON:  // ---------------------- polygon
5836       {
5837         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5838         int iNode = 0;
5839         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5840         while (itN->more()) {
5841           const SMDS_MeshNode* node =
5842             static_cast<const SMDS_MeshNode*>(itN->next());
5843           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5844           if (nodeMapIt == nodeMap.end())
5845             break; // not all nodes transformed
5846           if (needReverse) {
5847             // reverse mirrored faces and volumes
5848             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5849           } else {
5850             poly_nodes[iNode] = (*nodeMapIt).second;
5851           }
5852           iNode++;
5853         }
5854         if ( iNode != nbNodes )
5855           continue; // not all nodes transformed
5856
5857         if ( theTargetMesh ) {
5858           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5859           srcElems.Append( elem );
5860         }
5861         else if ( theCopy ) {
5862           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5863           srcElems.Append( elem );
5864         }
5865         else {
5866           aMesh->ChangePolygonNodes(elem, poly_nodes);
5867         }
5868       }
5869       break;
5870
5871     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5872       {
5873         const SMDS_VtkVolume* aPolyedre =
5874           dynamic_cast<const SMDS_VtkVolume*>( elem );
5875         if (!aPolyedre) {
5876           MESSAGE("Warning: bad volumic element");
5877           continue;
5878         }
5879
5880         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5881         vector<int> quantities; quantities.reserve( nbNodes );
5882
5883         bool allTransformed = true;
5884         int nbFaces = aPolyedre->NbFaces();
5885         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5886           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5887           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5888             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5889             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5890             if (nodeMapIt == nodeMap.end()) {
5891               allTransformed = false; // not all nodes transformed
5892             } else {
5893               poly_nodes.push_back((*nodeMapIt).second);
5894             }
5895             if ( needReverse && allTransformed )
5896               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5897           }
5898           quantities.push_back(nbFaceNodes);
5899         }
5900         if ( !allTransformed )
5901           continue; // not all nodes transformed
5902
5903         if ( theTargetMesh ) {
5904           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5905           srcElems.Append( elem );
5906         }
5907         else if ( theCopy ) {
5908           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5909           srcElems.Append( elem );
5910         }
5911         else {
5912           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5913         }
5914       }
5915       break;
5916
5917     case SMDSGeom_BALL: // -------------------- Ball
5918       {
5919         if ( !theCopy && !theTargetMesh ) continue;
5920
5921         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5922         if (nodeMapIt == nodeMap.end())
5923           continue; // not all nodes transformed
5924
5925         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5926         if ( theTargetMesh ) {
5927           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5928           srcElems.Append( elem );
5929         }
5930         else {
5931           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5932           srcElems.Append( elem );
5933         }
5934       }
5935       break;
5936
5937     default: // ----------------------- Regular elements
5938
5939       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5940       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5941       const std::vector<int>& i = needReverse ? iRev : iForw;
5942
5943       // find transformed nodes
5944       vector<const SMDS_MeshNode*> nodes(nbNodes);
5945       int iNode = 0;
5946       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5947       while ( itN->more() ) {
5948         const SMDS_MeshNode* node =
5949           static_cast<const SMDS_MeshNode*>( itN->next() );
5950         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5951         if ( nodeMapIt == nodeMap.end() )
5952           break; // not all nodes transformed
5953         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5954       }
5955       if ( iNode != nbNodes )
5956         continue; // not all nodes transformed
5957
5958       if ( theTargetMesh ) {
5959         if ( SMDS_MeshElement* copy =
5960              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5961           myLastCreatedElems.Append( copy );
5962           srcElems.Append( elem );
5963         }
5964       }
5965       else if ( theCopy ) {
5966         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5967           srcElems.Append( elem );
5968       }
5969       else {
5970         // reverse element as it was reversed by transformation
5971         if ( nbNodes > 2 )
5972           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5973       }
5974     } // switch ( geomType )
5975
5976   } // loop on elements
5977
5978   PGroupIDs newGroupIDs;
5979
5980   if ( ( theMakeGroups && theCopy ) ||
5981        ( theMakeGroups && theTargetMesh ) )
5982     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5983
5984   return newGroupIDs;
5985 }
5986
5987 //=======================================================================
5988 /*!
5989  * \brief Create groups of elements made during transformation
5990  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5991  * \param elemGens - elements making corresponding myLastCreatedElems
5992  * \param postfix - to append to names of new groups
5993  */
5994 //=======================================================================
5995
5996 SMESH_MeshEditor::PGroupIDs
5997 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5998                                  const SMESH_SequenceOfElemPtr& elemGens,
5999                                  const std::string&             postfix,
6000                                  SMESH_Mesh*                    targetMesh)
6001 {
6002   PGroupIDs newGroupIDs( new list<int> );
6003   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6004
6005   // Sort existing groups by types and collect their names
6006
6007   // to store an old group and a generated new ones
6008   using boost::tuple;
6009   using boost::make_tuple;
6010   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6011   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6012   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6013   // group names
6014   set< string > groupNames;
6015
6016   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6017   if ( !groupIt->more() ) return newGroupIDs;
6018
6019   int newGroupID = mesh->GetGroupIds().back()+1;
6020   while ( groupIt->more() )
6021   {
6022     SMESH_Group * group = groupIt->next();
6023     if ( !group ) continue;
6024     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6025     if ( !groupDS || groupDS->IsEmpty() ) continue;
6026     groupNames.insert    ( group->GetName() );
6027     groupDS->SetStoreName( group->GetName() );
6028     const SMDSAbs_ElementType type = groupDS->GetType();
6029     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6030     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6031     groupsByType[ groupDS->GetType() ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6032     orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
6033   }
6034
6035   // Loop on nodes and elements to add them in new groups
6036
6037   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6038   {
6039     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6040     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6041     if ( gens.Length() != elems.Length() )
6042       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6043
6044     // loop on created elements
6045     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6046     {
6047       const SMDS_MeshElement* sourceElem = gens( iElem );
6048       if ( !sourceElem ) {
6049         MESSAGE("generateGroups(): NULL source element");
6050         continue;
6051       }
6052       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6053       if ( groupsOldNew.empty() ) { // no groups of this type at all
6054         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6055           ++iElem; // skip all elements made by sourceElem
6056         continue;
6057       }
6058       // collect all elements made by the iElem-th sourceElem
6059       list< const SMDS_MeshElement* > resultElems;
6060       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6061         if ( resElem != sourceElem )
6062           resultElems.push_back( resElem );
6063       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6064         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6065           if ( resElem != sourceElem )
6066             resultElems.push_back( resElem );
6067
6068       // there must be a top element
6069       const SMDS_MeshElement* topElem = 0;
6070       if ( isNodes )
6071       {
6072         topElem = resultElems.back();
6073         resultElems.pop_back();
6074       }
6075       else
6076       {
6077         list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6078         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6079           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6080           {
6081             topElem = *resElemIt;
6082             resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6083             break;
6084           }
6085       }
6086
6087       // add resultElems to groups originted from ones the sourceElem belongs to
6088       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6089       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6090       {
6091         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6092         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6093         {
6094           // fill in a new group
6095           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6096           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6097           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6098             newGroup.Add( *resElemIt );
6099
6100           // fill a "top" group
6101           if ( topElem )
6102           {
6103             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6104             newTopGroup.Add( topElem );
6105           }
6106         }
6107       }
6108     } // loop on created elements
6109   }// loop on nodes and elements
6110
6111   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6112
6113   list<int> topGrouIds;
6114   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6115   {
6116     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6117     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6118                                       orderedOldNewGroups[i]->get<2>() };
6119     const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6120     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6121     {
6122       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6123       if ( newGroupDS->IsEmpty() )
6124       {
6125         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6126       }
6127       else
6128       {
6129         // set group type
6130         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6131
6132         // make a name
6133         const bool isTop = ( nbNewGroups == 2 &&
6134                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6135                              is2nd );
6136
6137         string name = oldGroupDS->GetStoreName();
6138         if ( !targetMesh ) {
6139           string suffix = ( isTop ? "top": postfix.c_str() );
6140           name += "_";
6141           name += suffix;
6142           int nb = 1;
6143           while ( !groupNames.insert( name ).second ) // name exists
6144             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6145         }
6146         else if ( isTop ) {
6147           name += "_top";
6148         }
6149         newGroupDS->SetStoreName( name.c_str() );
6150
6151         // make a SMESH_Groups
6152         mesh->AddGroup( newGroupDS );
6153         if ( isTop )
6154           topGrouIds.push_back( newGroupDS->GetID() );
6155         else
6156           newGroupIDs->push_back( newGroupDS->GetID() );
6157       }
6158     }
6159   }
6160   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6161
6162   return newGroupIDs;
6163 }
6164
6165 //================================================================================
6166 /*!
6167  * \brief Return list of group of nodes close to each other within theTolerance
6168  *        Search among theNodes or in the whole mesh if theNodes is empty using
6169  *        an Octree algorithm
6170  */
6171 //================================================================================
6172
6173 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6174                                             const double         theTolerance,
6175                                             TListOfListOfNodes & theGroupsOfNodes)
6176 {
6177   myLastCreatedElems.Clear();
6178   myLastCreatedNodes.Clear();
6179
6180   if ( theNodes.empty() )
6181   { // get all nodes in the mesh
6182     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6183     while ( nIt->more() )
6184       theNodes.insert( theNodes.end(),nIt->next());
6185   }
6186
6187   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6188 }
6189
6190
6191 //=======================================================================
6192 /*!
6193  * \brief Implementation of search for the node closest to point
6194  */
6195 //=======================================================================
6196
6197 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6198 {
6199   //---------------------------------------------------------------------
6200   /*!
6201    * \brief Constructor
6202    */
6203   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6204   {
6205     myMesh = ( SMESHDS_Mesh* ) theMesh;
6206
6207     TIDSortedNodeSet nodes;
6208     if ( theMesh ) {
6209       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6210       while ( nIt->more() )
6211         nodes.insert( nodes.end(), nIt->next() );
6212     }
6213     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6214
6215     // get max size of a leaf box
6216     SMESH_OctreeNode* tree = myOctreeNode;
6217     while ( !tree->isLeaf() )
6218     {
6219       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6220       if ( cIt->more() )
6221         tree = cIt->next();
6222     }
6223     myHalfLeafSize = tree->maxSize() / 2.;
6224   }
6225
6226   //---------------------------------------------------------------------
6227   /*!
6228    * \brief Move node and update myOctreeNode accordingly
6229    */
6230   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6231   {
6232     myOctreeNode->UpdateByMoveNode( node, toPnt );
6233     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6234   }
6235
6236   //---------------------------------------------------------------------
6237   /*!
6238    * \brief Do it's job
6239    */
6240   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6241   {
6242     map<double, const SMDS_MeshNode*> dist2Nodes;
6243     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6244     if ( !dist2Nodes.empty() )
6245       return dist2Nodes.begin()->second;
6246     list<const SMDS_MeshNode*> nodes;
6247     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6248
6249     double minSqDist = DBL_MAX;
6250     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6251     {
6252       // sort leafs by their distance from thePnt
6253       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6254       TDistTreeMap treeMap;
6255       list< SMESH_OctreeNode* > treeList;
6256       list< SMESH_OctreeNode* >::iterator trIt;
6257       treeList.push_back( myOctreeNode );
6258
6259       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6260       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6261       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6262       {
6263         SMESH_OctreeNode* tree = *trIt;
6264         if ( !tree->isLeaf() ) // put children to the queue
6265         {
6266           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6267           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6268           while ( cIt->more() )
6269             treeList.push_back( cIt->next() );
6270         }
6271         else if ( tree->NbNodes() ) // put a tree to the treeMap
6272         {
6273           const Bnd_B3d& box = *tree->getBox();
6274           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6275           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6276           if ( !it_in.second ) // not unique distance to box center
6277             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6278         }
6279       }
6280       // find distance after which there is no sense to check tree's
6281       double sqLimit = DBL_MAX;
6282       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6283       if ( treeMap.size() > 5 ) {
6284         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6285         const Bnd_B3d& box = *closestTree->getBox();
6286         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6287         sqLimit = limit * limit;
6288       }
6289       // get all nodes from trees
6290       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6291         if ( sqDist_tree->first > sqLimit )
6292           break;
6293         SMESH_OctreeNode* tree = sqDist_tree->second;
6294         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6295       }
6296     }
6297     // find closest among nodes
6298     minSqDist = DBL_MAX;
6299     const SMDS_MeshNode* closestNode = 0;
6300     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6301     for ( ; nIt != nodes.end(); ++nIt ) {
6302       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6303       if ( minSqDist > sqDist ) {
6304         closestNode = *nIt;
6305         minSqDist = sqDist;
6306       }
6307     }
6308     return closestNode;
6309   }
6310
6311   //---------------------------------------------------------------------
6312   /*!
6313    * \brief Destructor
6314    */
6315   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6316
6317   //---------------------------------------------------------------------
6318   /*!
6319    * \brief Return the node tree
6320    */
6321   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6322
6323 private:
6324   SMESH_OctreeNode* myOctreeNode;
6325   SMESHDS_Mesh*     myMesh;
6326   double            myHalfLeafSize; // max size of a leaf box
6327 };
6328
6329 //=======================================================================
6330 /*!
6331  * \brief Return SMESH_NodeSearcher
6332  */
6333 //=======================================================================
6334
6335 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6336 {
6337   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6338 }
6339
6340 // ========================================================================
6341 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6342 {
6343   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6344   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6345   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6346
6347   //=======================================================================
6348   /*!
6349    * \brief Octal tree of bounding boxes of elements
6350    */
6351   //=======================================================================
6352
6353   class ElementBndBoxTree : public SMESH_Octree
6354   {
6355   public:
6356
6357     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6358                       SMDSAbs_ElementType  elemType,
6359                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6360                       double               tolerance = NodeRadius );
6361     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6362     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6363     void getElementsInSphere ( const gp_XYZ& center,
6364                                const double  radius, TIDSortedElemSet& foundElems);
6365     size_t getSize() { return std::max( _size, _elements.size() ); }
6366     ~ElementBndBoxTree();
6367
6368   protected:
6369     ElementBndBoxTree():_size(0) {}
6370     SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6371     void          buildChildrenData();
6372     Bnd_B3d*      buildRootBox();
6373   private:
6374     //!< Bounding box of element
6375     struct ElementBox : public Bnd_B3d
6376     {
6377       const SMDS_MeshElement* _element;
6378       int                     _refCount; // an ElementBox can be included in several tree branches
6379       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6380     };
6381     vector< ElementBox* > _elements;
6382     size_t                _size;
6383   };
6384
6385   //================================================================================
6386   /*!
6387    * \brief ElementBndBoxTree creation
6388    */
6389   //================================================================================
6390
6391   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6392     :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6393   {
6394     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6395     _elements.reserve( nbElems );
6396
6397     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6398     while ( elemIt->more() )
6399       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6400
6401     compute();
6402   }
6403
6404   //================================================================================
6405   /*!
6406    * \brief Destructor
6407    */
6408   //================================================================================
6409
6410   ElementBndBoxTree::~ElementBndBoxTree()
6411   {
6412     for ( int i = 0; i < _elements.size(); ++i )
6413       if ( --_elements[i]->_refCount <= 0 )
6414         delete _elements[i];
6415   }
6416
6417   //================================================================================
6418   /*!
6419    * \brief Return the maximal box
6420    */
6421   //================================================================================
6422
6423   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6424   {
6425     Bnd_B3d* box = new Bnd_B3d;
6426     for ( int i = 0; i < _elements.size(); ++i )
6427       box->Add( *_elements[i] );
6428     return box;
6429   }
6430
6431   //================================================================================
6432   /*!
6433    * \brief Redistrubute element boxes among children
6434    */
6435   //================================================================================
6436
6437   void ElementBndBoxTree::buildChildrenData()
6438   {
6439     for ( int i = 0; i < _elements.size(); ++i )
6440     {
6441       for (int j = 0; j < 8; j++)
6442       {
6443         if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6444         {
6445           _elements[i]->_refCount++;
6446           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6447         }
6448       }
6449       _elements[i]->_refCount--;
6450     }
6451     _size = _elements.size();
6452     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6453
6454     for (int j = 0; j < 8; j++)
6455     {
6456       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6457       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6458         child->myIsLeaf = true;
6459
6460       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6461         SMESHUtils::CompactVector( child->_elements );
6462     }
6463   }
6464
6465   //================================================================================
6466   /*!
6467    * \brief Return elements which can include the point
6468    */
6469   //================================================================================
6470
6471   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6472                                                 TIDSortedElemSet& foundElems)
6473   {
6474     if ( getBox()->IsOut( point.XYZ() ))
6475       return;
6476
6477     if ( isLeaf() )
6478     {
6479       for ( int i = 0; i < _elements.size(); ++i )
6480         if ( !_elements[i]->IsOut( point.XYZ() ))
6481           foundElems.insert( _elements[i]->_element );
6482     }
6483     else
6484     {
6485       for (int i = 0; i < 8; i++)
6486         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6487     }
6488   }
6489
6490   //================================================================================
6491   /*!
6492    * \brief Return elements which can be intersected by the line
6493    */
6494   //================================================================================
6495
6496   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6497                                                TIDSortedElemSet& foundElems)
6498   {
6499     if ( getBox()->IsOut( line ))
6500       return;
6501
6502     if ( isLeaf() )
6503     {
6504       for ( int i = 0; i < _elements.size(); ++i )
6505         if ( !_elements[i]->IsOut( line ))
6506           foundElems.insert( _elements[i]->_element );
6507     }
6508     else
6509     {
6510       for (int i = 0; i < 8; i++)
6511         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6512     }
6513   }
6514
6515   //================================================================================
6516   /*!
6517    * \brief Return elements from leaves intersecting the sphere
6518    */
6519   //================================================================================
6520
6521   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6522                                                 const double      radius,
6523                                                 TIDSortedElemSet& foundElems)
6524   {
6525     if ( getBox()->IsOut( center, radius ))
6526       return;
6527
6528     if ( isLeaf() )
6529     {
6530       for ( int i = 0; i < _elements.size(); ++i )
6531         if ( !_elements[i]->IsOut( center, radius ))
6532           foundElems.insert( _elements[i]->_element );
6533     }
6534     else
6535     {
6536       for (int i = 0; i < 8; i++)
6537         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6538     }
6539   }
6540
6541   //================================================================================
6542   /*!
6543    * \brief Construct the element box
6544    */
6545   //================================================================================
6546
6547   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6548   {
6549     _element  = elem;
6550     _refCount = 1;
6551     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6552     while ( nIt->more() )
6553       Add( SMESH_TNodeXYZ( nIt->next() ));
6554     Enlarge( tolerance );
6555   }
6556
6557 } // namespace
6558
6559 //=======================================================================
6560 /*!
6561  * \brief Implementation of search for the elements by point and
6562  *        of classification of point in 2D mesh
6563  */
6564 //=======================================================================
6565
6566 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6567 {
6568   SMESHDS_Mesh*                _mesh;
6569   SMDS_ElemIteratorPtr         _meshPartIt;
6570   ElementBndBoxTree*           _ebbTree;
6571   SMESH_NodeSearcherImpl*      _nodeSearcher;
6572   SMDSAbs_ElementType          _elementType;
6573   double                       _tolerance;
6574   bool                         _outerFacesFound;
6575   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6576
6577   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6578     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6579   ~SMESH_ElementSearcherImpl()
6580   {
6581     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6582     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6583   }
6584   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6585                                   SMDSAbs_ElementType                type,
6586                                   vector< const SMDS_MeshElement* >& foundElements);
6587   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6588   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6589                                                  SMDSAbs_ElementType type );
6590
6591   void GetElementsNearLine( const gp_Ax1&                      line,
6592                             SMDSAbs_ElementType                type,
6593                             vector< const SMDS_MeshElement* >& foundElems);
6594   double getTolerance();
6595   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6596                             const double tolerance, double & param);
6597   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6598   bool isOuterBoundary(const SMDS_MeshElement* face) const
6599   {
6600     return _outerFaces.empty() || _outerFaces.count(face);
6601   }
6602   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6603   {
6604     const SMDS_MeshElement* _face;
6605     gp_Vec                  _faceNorm;
6606     bool                    _coincides; //!< the line lays in face plane
6607     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6608       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6609   };
6610   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6611   {
6612     SMESH_TLink      _link;
6613     TIDSortedElemSet _faces;
6614     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6615       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6616   };
6617 };
6618
6619 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6620 {
6621   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6622              << ", _coincides="<<i._coincides << ")";
6623 }
6624
6625 //=======================================================================
6626 /*!
6627  * \brief define tolerance for search
6628  */
6629 //=======================================================================
6630
6631 double SMESH_ElementSearcherImpl::getTolerance()
6632 {
6633   if ( _tolerance < 0 )
6634   {
6635     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6636
6637     _tolerance = 0;
6638     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6639     {
6640       double boxSize = _nodeSearcher->getTree()->maxSize();
6641       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6642     }
6643     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6644     {
6645       double boxSize = _ebbTree->maxSize();
6646       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6647     }
6648     if ( _tolerance == 0 )
6649     {
6650       // define tolerance by size of a most complex element
6651       int complexType = SMDSAbs_Volume;
6652       while ( complexType > SMDSAbs_All &&
6653               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6654         --complexType;
6655       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6656       double elemSize;
6657       if ( complexType == int( SMDSAbs_Node ))
6658       {
6659         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6660         elemSize = 1;
6661         if ( meshInfo.NbNodes() > 2 )
6662           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6663       }
6664       else
6665       {
6666         SMDS_ElemIteratorPtr elemIt =
6667             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6668         const SMDS_MeshElement* elem = elemIt->next();
6669         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6670         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6671         elemSize = 0;
6672         while ( nodeIt->more() )
6673         {
6674           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6675           elemSize = max( dist, elemSize );
6676         }
6677       }
6678       _tolerance = 1e-4 * elemSize;
6679     }
6680   }
6681   return _tolerance;
6682 }
6683
6684 //================================================================================
6685 /*!
6686  * \brief Find intersection of the line and an edge of face and return parameter on line
6687  */
6688 //================================================================================
6689
6690 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6691                                                      const SMDS_MeshElement* face,
6692                                                      const double            tol,
6693                                                      double &                param)
6694 {
6695   int nbInts = 0;
6696   param = 0;
6697
6698   GeomAPI_ExtremaCurveCurve anExtCC;
6699   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6700
6701   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6702   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6703   {
6704     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6705                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6706     anExtCC.Init( lineCurve, edge);
6707     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6708     {
6709       Quantity_Parameter pl, pe;
6710       anExtCC.LowerDistanceParameters( pl, pe );
6711       param += pl;
6712       if ( ++nbInts == 2 )
6713         break;
6714     }
6715   }
6716   if ( nbInts > 0 ) param /= nbInts;
6717   return nbInts > 0;
6718 }
6719 //================================================================================
6720 /*!
6721  * \brief Find all faces belonging to the outer boundary of mesh
6722  */
6723 //================================================================================
6724
6725 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6726 {
6727   if ( _outerFacesFound ) return;
6728
6729   // Collect all outer faces by passing from one outer face to another via their links
6730   // and BTW find out if there are internal faces at all.
6731
6732   // checked links and links where outer boundary meets internal one
6733   set< SMESH_TLink > visitedLinks, seamLinks;
6734
6735   // links to treat with already visited faces sharing them
6736   list < TFaceLink > startLinks;
6737
6738   // load startLinks with the first outerFace
6739   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6740   _outerFaces.insert( outerFace );
6741
6742   TIDSortedElemSet emptySet;
6743   while ( !startLinks.empty() )
6744   {
6745     const SMESH_TLink& link  = startLinks.front()._link;
6746     TIDSortedElemSet&  faces = startLinks.front()._faces;
6747
6748     outerFace = *faces.begin();
6749     // find other faces sharing the link
6750     const SMDS_MeshElement* f;
6751     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6752       faces.insert( f );
6753
6754     // select another outer face among the found
6755     const SMDS_MeshElement* outerFace2 = 0;
6756     if ( faces.size() == 2 )
6757     {
6758       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6759     }
6760     else if ( faces.size() > 2 )
6761     {
6762       seamLinks.insert( link );
6763
6764       // link direction within the outerFace
6765       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6766                    SMESH_TNodeXYZ( link.node2()));
6767       int i1 = outerFace->GetNodeIndex( link.node1() );
6768       int i2 = outerFace->GetNodeIndex( link.node2() );
6769       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6770       if ( rev ) n1n2.Reverse();
6771       // outerFace normal
6772       gp_XYZ ofNorm, fNorm;
6773       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6774       {
6775         // direction from the link inside outerFace
6776         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6777         // sort all other faces by angle with the dirInOF
6778         map< double, const SMDS_MeshElement* > angle2Face;
6779         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6780         for ( ; face != faces.end(); ++face )
6781         {
6782           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6783             continue;
6784           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6785           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6786           if ( angle < 0 ) angle += 2. * M_PI;
6787           angle2Face.insert( make_pair( angle, *face ));
6788         }
6789         if ( !angle2Face.empty() )
6790           outerFace2 = angle2Face.begin()->second;
6791       }
6792     }
6793     // store the found outer face and add its links to continue seaching from
6794     if ( outerFace2 )
6795     {
6796       _outerFaces.insert( outerFace );
6797       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6798       for ( int i = 0; i < nbNodes; ++i )
6799       {
6800         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6801         if ( visitedLinks.insert( link2 ).second )
6802           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6803       }
6804     }
6805     startLinks.pop_front();
6806   }
6807   _outerFacesFound = true;
6808
6809   if ( !seamLinks.empty() )
6810   {
6811     // There are internal boundaries touching the outher one,
6812     // find all faces of internal boundaries in order to find
6813     // faces of boundaries of holes, if any.
6814
6815   }
6816   else
6817   {
6818     _outerFaces.clear();
6819   }
6820 }
6821
6822 //=======================================================================
6823 /*!
6824  * \brief Find elements of given type where the given point is IN or ON.
6825  *        Returns nb of found elements and elements them-selves.
6826  *
6827  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6828  */
6829 //=======================================================================
6830
6831 int SMESH_ElementSearcherImpl::
6832 FindElementsByPoint(const gp_Pnt&                      point,
6833                     SMDSAbs_ElementType                type,
6834                     vector< const SMDS_MeshElement* >& foundElements)
6835 {
6836   foundElements.clear();
6837
6838   double tolerance = getTolerance();
6839
6840   // =================================================================================
6841   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6842   {
6843     if ( !_nodeSearcher )
6844       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6845
6846     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6847     if ( !closeNode ) return foundElements.size();
6848
6849     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6850       return foundElements.size(); // to far from any node
6851
6852     if ( type == SMDSAbs_Node )
6853     {
6854       foundElements.push_back( closeNode );
6855     }
6856     else
6857     {
6858       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6859       while ( elemIt->more() )
6860         foundElements.push_back( elemIt->next() );
6861     }
6862   }
6863   // =================================================================================
6864   else // elements more complex than 0D
6865   {
6866     if ( !_ebbTree || _elementType != type )
6867     {
6868       if ( _ebbTree ) delete _ebbTree;
6869       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6870     }
6871     TIDSortedElemSet suspectElems;
6872     _ebbTree->getElementsNearPoint( point, suspectElems );
6873     TIDSortedElemSet::iterator elem = suspectElems.begin();
6874     for ( ; elem != suspectElems.end(); ++elem )
6875       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6876         foundElements.push_back( *elem );
6877   }
6878   return foundElements.size();
6879 }
6880
6881 //=======================================================================
6882 /*!
6883  * \brief Find an element of given type most close to the given point
6884  *
6885  * WARNING: Only face search is implemeneted so far
6886  */
6887 //=======================================================================
6888
6889 const SMDS_MeshElement*
6890 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6891                                           SMDSAbs_ElementType type )
6892 {
6893   const SMDS_MeshElement* closestElem = 0;
6894
6895   if ( type == SMDSAbs_Face )
6896   {
6897     if ( !_ebbTree || _elementType != type )
6898     {
6899       if ( _ebbTree ) delete _ebbTree;
6900       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6901     }
6902     TIDSortedElemSet suspectElems;
6903     _ebbTree->getElementsNearPoint( point, suspectElems );
6904
6905     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6906     {
6907       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6908                                  _ebbTree->getBox()->CornerMax() );
6909       double radius;
6910       if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6911         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6912       else
6913         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6914       while ( suspectElems.empty() )
6915       {
6916         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6917         radius *= 1.1;
6918       }
6919     }
6920     double minDist = std::numeric_limits<double>::max();
6921     multimap< double, const SMDS_MeshElement* > dist2face;
6922     TIDSortedElemSet::iterator elem = suspectElems.begin();
6923     for ( ; elem != suspectElems.end(); ++elem )
6924     {
6925       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6926                                                    point );
6927       if ( dist < minDist + 1e-10)
6928       {
6929         minDist = dist;
6930         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6931       }
6932     }
6933     if ( !dist2face.empty() )
6934     {
6935       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6936       closestElem = d2f->second;
6937       // if there are several elements at the same distance, select one
6938       // with GC closest to the point
6939       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6940       double minDistToGC = 0;
6941       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6942       {
6943         if ( minDistToGC == 0 )
6944         {
6945           gp_XYZ gc(0,0,0);
6946           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6947                            TXyzIterator(), gc ) / closestElem->NbNodes();
6948           minDistToGC = point.SquareDistance( gc );
6949         }
6950         gp_XYZ gc(0,0,0);
6951         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6952                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6953         double d = point.SquareDistance( gc );
6954         if ( d < minDistToGC )
6955         {
6956           minDistToGC = d;
6957           closestElem = d2f->second;
6958         }
6959       }
6960       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6961       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6962     }
6963   }
6964   else
6965   {
6966     // NOT IMPLEMENTED SO FAR
6967   }
6968   return closestElem;
6969 }
6970
6971
6972 //================================================================================
6973 /*!
6974  * \brief Classify the given point in the closed 2D mesh
6975  */
6976 //================================================================================
6977
6978 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6979 {
6980   double tolerance = getTolerance();
6981   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6982   {
6983     if ( _ebbTree ) delete _ebbTree;
6984     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6985   }
6986   // Algo: analyse transition of a line starting at the point through mesh boundary;
6987   // try three lines parallel to axis of the coordinate system and perform rough
6988   // analysis. If solution is not clear perform thorough analysis.
6989
6990   const int nbAxes = 3;
6991   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6992   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6993   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6994   multimap< int, int > nbInt2Axis; // to find the simplest case
6995   for ( int axis = 0; axis < nbAxes; ++axis )
6996   {
6997     gp_Ax1 lineAxis( point, axisDir[axis]);
6998     gp_Lin line    ( lineAxis );
6999
7000     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
7001     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
7002
7003     // Intersect faces with the line
7004
7005     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7006     TIDSortedElemSet::iterator face = suspectFaces.begin();
7007     for ( ; face != suspectFaces.end(); ++face )
7008     {
7009       // get face plane
7010       gp_XYZ fNorm;
7011       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
7012       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
7013
7014       // perform intersection
7015       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
7016       if ( !intersection.IsDone() )
7017         continue;
7018       if ( intersection.IsInQuadric() )
7019       {
7020         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
7021       }
7022       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
7023       {
7024         gp_Pnt intersectionPoint = intersection.Point(1);
7025         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
7026           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
7027       }
7028     }
7029     // Analyse intersections roughly
7030
7031     int nbInter = u2inters.size();
7032     if ( nbInter == 0 )
7033       return TopAbs_OUT;
7034
7035     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
7036     if ( nbInter == 1 ) // not closed mesh
7037       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7038
7039     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7040       return TopAbs_ON;
7041
7042     if ( (f<0) == (l<0) )
7043       return TopAbs_OUT;
7044
7045     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
7046     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
7047     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7048       return TopAbs_IN;
7049
7050     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7051
7052     if ( _outerFacesFound ) break; // pass to thorough analysis
7053
7054   } // three attempts - loop on CS axes
7055
7056   // Analyse intersections thoroughly.
7057   // We make two loops maximum, on the first one we only exclude touching intersections,
7058   // on the second, if situation is still unclear, we gather and use information on
7059   // position of faces (internal or outer). If faces position is already gathered,
7060   // we make the second loop right away.
7061
7062   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7063   {
7064     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7065     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7066     {
7067       int axis = nb_axis->second;
7068       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7069
7070       gp_Ax1 lineAxis( point, axisDir[axis]);
7071       gp_Lin line    ( lineAxis );
7072
7073       // add tangent intersections to u2inters
7074       double param;
7075       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7076       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7077         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7078           u2inters.insert(make_pair( param, *tgtInt ));
7079       tangentInters[ axis ].clear();
7080
7081       // Count intersections before and after the point excluding touching ones.
7082       // If hasPositionInfo we count intersections of outer boundary only
7083
7084       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7085       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7086       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7087       bool ok = ! u_int1->second._coincides;
7088       while ( ok && u_int1 != u2inters.end() )
7089       {
7090         double u = u_int1->first;
7091         bool touchingInt = false;
7092         if ( ++u_int2 != u2inters.end() )
7093         {
7094           // skip intersections at the same point (if the line passes through edge or node)
7095           int nbSamePnt = 0;
7096           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7097           {
7098             ++nbSamePnt;
7099             ++u_int2;
7100           }
7101
7102           // skip tangent intersections
7103           int nbTgt = 0;
7104           const SMDS_MeshElement* prevFace = u_int1->second._face;
7105           while ( ok && u_int2->second._coincides )
7106           {
7107             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7108               ok = false;
7109             else
7110             {
7111               nbTgt++;
7112               u_int2++;
7113               ok = ( u_int2 != u2inters.end() );
7114             }
7115           }
7116           if ( !ok ) break;
7117
7118           // skip intersections at the same point after tangent intersections
7119           if ( nbTgt > 0 )
7120           {
7121             double u2 = u_int2->first;
7122             ++u_int2;
7123             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7124             {
7125               ++nbSamePnt;
7126               ++u_int2;
7127             }
7128           }
7129           // decide if we skipped a touching intersection
7130           if ( nbSamePnt + nbTgt > 0 )
7131           {
7132             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7133             map< double, TInters >::iterator u_int = u_int1;
7134             for ( ; u_int != u_int2; ++u_int )
7135             {
7136               if ( u_int->second._coincides ) continue;
7137               double dot = u_int->second._faceNorm * line.Direction();
7138               if ( dot > maxDot ) maxDot = dot;
7139               if ( dot < minDot ) minDot = dot;
7140             }
7141             touchingInt = ( minDot*maxDot < 0 );
7142           }
7143         }
7144         if ( !touchingInt )
7145         {
7146           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7147           {
7148             if ( u < 0 )
7149               ++nbIntBeforePoint;
7150             else
7151               ++nbIntAfterPoint;
7152           }
7153           if ( u < f ) f = u;
7154           if ( u > l ) l = u;
7155         }
7156
7157         u_int1 = u_int2; // to next intersection
7158
7159       } // loop on intersections with one line
7160
7161       if ( ok )
7162       {
7163         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7164           return TopAbs_ON;
7165
7166         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
7167           return TopAbs_OUT;
7168
7169         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7170           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7171
7172         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7173           return TopAbs_IN;
7174
7175         if ( (f<0) == (l<0) )
7176           return TopAbs_OUT;
7177
7178         if ( hasPositionInfo )
7179           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7180       }
7181     } // loop on intersections of the tree lines - thorough analysis
7182
7183     if ( !hasPositionInfo )
7184     {
7185       // gather info on faces position - is face in the outer boundary or not
7186       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7187       findOuterBoundary( u2inters.begin()->second._face );
7188     }
7189
7190   } // two attempts - with and w/o faces position info in the mesh
7191
7192   return TopAbs_UNKNOWN;
7193 }
7194
7195 //=======================================================================
7196 /*!
7197  * \brief Return elements possibly intersecting the line
7198  */
7199 //=======================================================================
7200
7201 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7202                                                      SMDSAbs_ElementType                type,
7203                                                      vector< const SMDS_MeshElement* >& foundElems)
7204 {
7205   if ( !_ebbTree || _elementType != type )
7206   {
7207     if ( _ebbTree ) delete _ebbTree;
7208     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7209   }
7210   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7211   _ebbTree->getElementsNearLine( line, suspectFaces );
7212   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7213 }
7214
7215 //=======================================================================
7216 /*!
7217  * \brief Return SMESH_ElementSearcher
7218  */
7219 //=======================================================================
7220
7221 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7222 {
7223   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7224 }
7225
7226 //=======================================================================
7227 /*!
7228  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7229  */
7230 //=======================================================================
7231
7232 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7233 {
7234   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7235 }
7236
7237 //=======================================================================
7238 /*!
7239  * \brief Return true if the point is IN or ON of the element
7240  */
7241 //=======================================================================
7242
7243 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7244 {
7245   if ( element->GetType() == SMDSAbs_Volume)
7246   {
7247     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7248   }
7249
7250   // get ordered nodes
7251
7252   vector< gp_XYZ > xyz;
7253   vector<const SMDS_MeshNode*> nodeList;
7254
7255   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7256   if ( element->IsQuadratic() ) {
7257     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7258       nodeIt = f->interlacedNodesElemIterator();
7259     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7260       nodeIt = e->interlacedNodesElemIterator();
7261   }
7262   while ( nodeIt->more() )
7263     {
7264       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7265       xyz.push_back( SMESH_TNodeXYZ(node) );
7266       nodeList.push_back(node);
7267     }
7268
7269   int i, nbNodes = element->NbNodes();
7270
7271   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7272   {
7273     // compute face normal
7274     gp_Vec faceNorm(0,0,0);
7275     xyz.push_back( xyz.front() );
7276     nodeList.push_back( nodeList.front() );
7277     for ( i = 0; i < nbNodes; ++i )
7278     {
7279       gp_Vec edge1( xyz[i+1], xyz[i]);
7280       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7281       faceNorm += edge1 ^ edge2;
7282     }
7283     double normSize = faceNorm.Magnitude();
7284     if ( normSize <= tol )
7285     {
7286       // degenerated face: point is out if it is out of all face edges
7287       for ( i = 0; i < nbNodes; ++i )
7288       {
7289         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7290         if ( !IsOut( &edge, point, tol ))
7291           return false;
7292       }
7293       return true;
7294     }
7295     faceNorm /= normSize;
7296
7297     // check if the point lays on face plane
7298     gp_Vec n2p( xyz[0], point );
7299     if ( fabs( n2p * faceNorm ) > tol )
7300       return true; // not on face plane
7301
7302     // check if point is out of face boundary:
7303     // define it by closest transition of a ray point->infinity through face boundary
7304     // on the face plane.
7305     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7306     // to find intersections of the ray with the boundary.
7307     gp_Vec ray = n2p;
7308     gp_Vec plnNorm = ray ^ faceNorm;
7309     normSize = plnNorm.Magnitude();
7310     if ( normSize <= tol ) return false; // point coincides with the first node
7311     plnNorm /= normSize;
7312     // for each node of the face, compute its signed distance to the plane
7313     vector<double> dist( nbNodes + 1);
7314     for ( i = 0; i < nbNodes; ++i )
7315     {
7316       gp_Vec n2p( xyz[i], point );
7317       dist[i] = n2p * plnNorm;
7318     }
7319     dist.back() = dist.front();
7320     // find the closest intersection
7321     int    iClosest = -1;
7322     double rClosest, distClosest = 1e100;;
7323     gp_Pnt pClosest;
7324     for ( i = 0; i < nbNodes; ++i )
7325     {
7326       double r;
7327       if ( fabs( dist[i]) < tol )
7328         r = 0.;
7329       else if ( fabs( dist[i+1]) < tol )
7330         r = 1.;
7331       else if ( dist[i] * dist[i+1] < 0 )
7332         r = dist[i] / ( dist[i] - dist[i+1] );
7333       else
7334         continue; // no intersection
7335       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7336       gp_Vec p2int ( point, pInt);
7337       if ( p2int * ray > -tol ) // right half-space
7338       {
7339         double intDist = p2int.SquareMagnitude();
7340         if ( intDist < distClosest )
7341         {
7342           iClosest = i;
7343           rClosest = r;
7344           pClosest = pInt;
7345           distClosest = intDist;
7346         }
7347       }
7348     }
7349     if ( iClosest < 0 )
7350       return true; // no intesections - out
7351
7352     // analyse transition
7353     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7354     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7355     gp_Vec p2int ( point, pClosest );
7356     bool out = (edgeNorm * p2int) < -tol;
7357     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7358       return out;
7359
7360     // ray pass through a face node; analyze transition through an adjacent edge
7361     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7362     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7363     gp_Vec edgeAdjacent( p1, p2 );
7364     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7365     bool out2 = (edgeNorm2 * p2int) < -tol;
7366
7367     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7368     return covexCorner ? (out || out2) : (out && out2);
7369   }
7370   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7371   {
7372     // point is out of edge if it is NOT ON any straight part of edge
7373     // (we consider quadratic edge as being composed of two straight parts)
7374     for ( i = 1; i < nbNodes; ++i )
7375     {
7376       gp_Vec edge( xyz[i-1], xyz[i]);
7377       gp_Vec n1p ( xyz[i-1], point);
7378       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7379       if ( dist > tol )
7380         continue;
7381       gp_Vec n2p( xyz[i], point );
7382       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7383         continue;
7384       return false; // point is ON this part
7385     }
7386     return true;
7387   }
7388   // Node or 0D element -------------------------------------------------------------------------
7389   {
7390     gp_Vec n2p ( xyz[0], point );
7391     return n2p.Magnitude() <= tol;
7392   }
7393   return true;
7394 }
7395
7396 //=======================================================================
7397
7398 namespace
7399 {
7400   // Position of a point relative to a segment
7401   //            .           .
7402   //            .  LEFT     .
7403   //            .           .
7404   //  VERTEX 1  o----ON----->  VERTEX 2
7405   //            .           .
7406   //            .  RIGHT    .
7407   //            .           .
7408   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7409                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7410   struct PointPos
7411   {
7412     PositionName _name;
7413     int          _index; // index of vertex or segment
7414
7415     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7416     bool operator < (const PointPos& other ) const
7417     {
7418       if ( _name == other._name )
7419         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7420       return _name < other._name;
7421     }
7422   };
7423
7424   //================================================================================
7425   /*!
7426    * \brief Return of a point relative to a segment
7427    *  \param point2D      - the point to analyze position of
7428    *  \param xyVec        - end points of segments
7429    *  \param index0       - 0-based index of the first point of segment
7430    *  \param posToFindOut - flags of positions to detect
7431    *  \retval PointPos - point position
7432    */
7433   //================================================================================
7434
7435   PointPos getPointPosition( const gp_XY& point2D,
7436                              const gp_XY* segEnds,
7437                              const int    index0 = 0,
7438                              const int    posToFindOut = POS_ALL)
7439   {
7440     const gp_XY& p1 = segEnds[ index0   ];
7441     const gp_XY& p2 = segEnds[ index0+1 ];
7442     const gp_XY grad = p2 - p1;
7443
7444     if ( posToFindOut & POS_VERTEX )
7445     {
7446       // check if the point2D is at "vertex 1" zone
7447       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7448                                   p1.Y() + grad.X() ) };
7449       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7450         return PointPos( POS_VERTEX, index0 );
7451
7452       // check if the point2D is at "vertex 2" zone
7453       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7454                                   p2.Y() + grad.X() ) };
7455       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7456         return PointPos( POS_VERTEX, index0 + 1);
7457     }
7458     double edgeEquation =
7459       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7460     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7461   }
7462 }
7463
7464 //=======================================================================
7465 /*!
7466  * \brief Return minimal distance from a point to a face
7467  *
7468  * Currently we ignore non-planarity and 2nd order of face
7469  */
7470 //=======================================================================
7471
7472 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7473                                       const gp_Pnt&        point )
7474 {
7475   double badDistance = -1;
7476   if ( !face ) return badDistance;
7477
7478   // coordinates of nodes (medium nodes, if any, ignored)
7479   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7480   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7481   xyz.resize( face->NbCornerNodes()+1 );
7482
7483   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7484   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7485   gp_Trsf trsf;
7486   gp_Vec OZ ( xyz[0], xyz[1] );
7487   gp_Vec OX ( xyz[0], xyz[2] );
7488   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7489   {
7490     if ( xyz.size() < 4 ) return badDistance;
7491     OZ = gp_Vec ( xyz[0], xyz[2] );
7492     OX = gp_Vec ( xyz[0], xyz[3] );
7493   }
7494   gp_Ax3 tgtCS;
7495   try {
7496     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7497   }
7498   catch ( Standard_Failure ) {
7499     return badDistance;
7500   }
7501   trsf.SetTransformation( tgtCS );
7502
7503   // move all the nodes to 2D
7504   vector<gp_XY> xy( xyz.size() );
7505   for ( size_t i = 0;i < xyz.size()-1; ++i )
7506   {
7507     gp_XYZ p3d = xyz[i];
7508     trsf.Transforms( p3d );
7509     xy[i].SetCoord( p3d.X(), p3d.Z() );
7510   }
7511   xyz.back() = xyz.front();
7512   xy.back() = xy.front();
7513
7514   // // move the point in 2D
7515   gp_XYZ tmpPnt = point.XYZ();
7516   trsf.Transforms( tmpPnt );
7517   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7518
7519   // loop on segments of the face to analyze point position ralative to the face
7520   set< PointPos > pntPosSet;
7521   for ( size_t i = 1; i < xy.size(); ++i )
7522   {
7523     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7524     pntPosSet.insert( pos );
7525   }
7526
7527   // compute distance
7528   PointPos pos = *pntPosSet.begin();
7529   // cout << "Face " << face->GetID() << " DIST: ";
7530   switch ( pos._name )
7531   {
7532   case POS_LEFT: {
7533     // point is most close to a segment
7534     gp_Vec p0p1( point, xyz[ pos._index ] );
7535     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7536     p1p2.Normalize();
7537     double projDist = p0p1 * p1p2; // distance projected to the segment
7538     gp_Vec projVec = p1p2 * projDist;
7539     gp_Vec distVec = p0p1 - projVec;
7540     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7541     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7542     return distVec.Magnitude();
7543   }
7544   case POS_RIGHT: {
7545     // point is inside the face
7546     double distToFacePlane = tmpPnt.Y();
7547     // cout << distToFacePlane << ", INSIDE " << endl;
7548     return Abs( distToFacePlane );
7549   }
7550   case POS_VERTEX: {
7551     // point is most close to a node
7552     gp_Vec distVec( point, xyz[ pos._index ]);
7553     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7554     return distVec.Magnitude();
7555   }
7556   }
7557   return badDistance;
7558 }
7559
7560 //=======================================================================
7561 //function : SimplifyFace
7562 //purpose  :
7563 //=======================================================================
7564 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7565                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7566                                     vector<int>&                        quantities) const
7567 {
7568   int nbNodes = faceNodes.size();
7569
7570   if (nbNodes < 3)
7571     return 0;
7572
7573   set<const SMDS_MeshNode*> nodeSet;
7574
7575   // get simple seq of nodes
7576   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7577   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7578   int iSimple = 0, nbUnique = 0;
7579
7580   simpleNodes[iSimple++] = faceNodes[0];
7581   nbUnique++;
7582   for (int iCur = 1; iCur < nbNodes; iCur++) {
7583     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7584       simpleNodes[iSimple++] = faceNodes[iCur];
7585       if (nodeSet.insert( faceNodes[iCur] ).second)
7586         nbUnique++;
7587     }
7588   }
7589   int nbSimple = iSimple;
7590   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7591     nbSimple--;
7592     iSimple--;
7593   }
7594
7595   if (nbUnique < 3)
7596     return 0;
7597
7598   // separate loops
7599   int nbNew = 0;
7600   bool foundLoop = (nbSimple > nbUnique);
7601   while (foundLoop) {
7602     foundLoop = false;
7603     set<const SMDS_MeshNode*> loopSet;
7604     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7605       const SMDS_MeshNode* n = simpleNodes[iSimple];
7606       if (!loopSet.insert( n ).second) {
7607         foundLoop = true;
7608
7609         // separate loop
7610         int iC = 0, curLast = iSimple;
7611         for (; iC < curLast; iC++) {
7612           if (simpleNodes[iC] == n) break;
7613         }
7614         int loopLen = curLast - iC;
7615         if (loopLen > 2) {
7616           // create sub-element
7617           nbNew++;
7618           quantities.push_back(loopLen);
7619           for (; iC < curLast; iC++) {
7620             poly_nodes.push_back(simpleNodes[iC]);
7621           }
7622         }
7623         // shift the rest nodes (place from the first loop position)
7624         for (iC = curLast + 1; iC < nbSimple; iC++) {
7625           simpleNodes[iC - loopLen] = simpleNodes[iC];
7626         }
7627         nbSimple -= loopLen;
7628         iSimple -= loopLen;
7629       }
7630     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7631   } // while (foundLoop)
7632
7633   if (iSimple > 2) {
7634     nbNew++;
7635     quantities.push_back(iSimple);
7636     for (int i = 0; i < iSimple; i++)
7637       poly_nodes.push_back(simpleNodes[i]);
7638   }
7639
7640   return nbNew;
7641 }
7642
7643 //=======================================================================
7644 //function : MergeNodes
7645 //purpose  : In each group, the cdr of nodes are substituted by the first one
7646 //           in all elements.
7647 //=======================================================================
7648
7649 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7650 {
7651   MESSAGE("MergeNodes");
7652   myLastCreatedElems.Clear();
7653   myLastCreatedNodes.Clear();
7654
7655   SMESHDS_Mesh* aMesh = GetMeshDS();
7656
7657   TNodeNodeMap nodeNodeMap; // node to replace - new node
7658   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7659   list< int > rmElemIds, rmNodeIds;
7660
7661   // Fill nodeNodeMap and elems
7662
7663   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7664   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7665     list<const SMDS_MeshNode*>& nodes = *grIt;
7666     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7667     const SMDS_MeshNode* nToKeep = *nIt;
7668     //MESSAGE("node to keep " << nToKeep->GetID());
7669     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7670       const SMDS_MeshNode* nToRemove = *nIt;
7671       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7672       if ( nToRemove != nToKeep ) {
7673         //MESSAGE("  node to remove " << nToRemove->GetID());
7674         rmNodeIds.push_back( nToRemove->GetID() );
7675         AddToSameGroups( nToKeep, nToRemove, aMesh );
7676         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7677         // after MergeNodes() w/o creating node in place of merged ones.
7678         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7679         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7680           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7681             sm->SetIsAlwaysComputed( true );
7682       }
7683
7684       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7685       while ( invElemIt->more() ) {
7686         const SMDS_MeshElement* elem = invElemIt->next();
7687         elems.insert(elem);
7688       }
7689     }
7690   }
7691   // Change element nodes or remove an element
7692
7693   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7694   for ( ; eIt != elems.end(); eIt++ ) {
7695     const SMDS_MeshElement* elem = *eIt;
7696     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7697     int nbNodes = elem->NbNodes();
7698     int aShapeId = FindShape( elem );
7699
7700     set<const SMDS_MeshNode*> nodeSet;
7701     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7702     int iUnique = 0, iCur = 0, nbRepl = 0;
7703     vector<int> iRepl( nbNodes );
7704
7705     // get new seq of nodes
7706     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7707     while ( itN->more() ) {
7708       const SMDS_MeshNode* n =
7709         static_cast<const SMDS_MeshNode*>( itN->next() );
7710
7711       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7712       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7713         n = (*nnIt).second;
7714         // BUG 0020185: begin
7715         {
7716           bool stopRecur = false;
7717           set<const SMDS_MeshNode*> nodesRecur;
7718           nodesRecur.insert(n);
7719           while (!stopRecur) {
7720             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7721             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7722               n = (*nnIt_i).second;
7723               if (!nodesRecur.insert(n).second) {
7724                 // error: recursive dependancy
7725                 stopRecur = true;
7726               }
7727             }
7728             else
7729               stopRecur = true;
7730           }
7731         }
7732         // BUG 0020185: end
7733       }
7734       curNodes[ iCur ] = n;
7735       bool isUnique = nodeSet.insert( n ).second;
7736       if ( isUnique )
7737         uniqueNodes[ iUnique++ ] = n;
7738       else
7739         iRepl[ nbRepl++ ] = iCur;
7740       iCur++;
7741     }
7742
7743     // Analyse element topology after replacement
7744
7745     bool isOk = true;
7746     int nbUniqueNodes = nodeSet.size();
7747     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7748     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7749       // Polygons and Polyhedral volumes
7750       if (elem->IsPoly()) {
7751
7752         if (elem->GetType() == SMDSAbs_Face) {
7753           // Polygon
7754           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7755           int inode = 0;
7756           for (; inode < nbNodes; inode++) {
7757             face_nodes[inode] = curNodes[inode];
7758           }
7759
7760           vector<const SMDS_MeshNode *> polygons_nodes;
7761           vector<int> quantities;
7762           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7763           if (nbNew > 0) {
7764             inode = 0;
7765             for (int iface = 0; iface < nbNew; iface++) {
7766               int nbNodes = quantities[iface];
7767               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7768               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7769                 poly_nodes[ii] = polygons_nodes[inode];
7770               }
7771               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7772               myLastCreatedElems.Append(newElem);
7773               if (aShapeId)
7774                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7775             }
7776
7777             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7778             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7779             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7780             int quid =0;
7781             if (nbNew > 0) quid = nbNew - 1;
7782             vector<int> newquant(quantities.begin()+quid, quantities.end());
7783             const SMDS_MeshElement* newElem = 0;
7784             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7785             myLastCreatedElems.Append(newElem);
7786             if ( aShapeId && newElem )
7787               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7788             rmElemIds.push_back(elem->GetID());
7789           }
7790           else {
7791             rmElemIds.push_back(elem->GetID());
7792           }
7793
7794         }
7795         else if (elem->GetType() == SMDSAbs_Volume) {
7796           // Polyhedral volume
7797           if (nbUniqueNodes < 4) {
7798             rmElemIds.push_back(elem->GetID());
7799           }
7800           else {
7801             // each face has to be analyzed in order to check volume validity
7802             const SMDS_VtkVolume* aPolyedre =
7803               dynamic_cast<const SMDS_VtkVolume*>( elem );
7804             if (aPolyedre) {
7805               int nbFaces = aPolyedre->NbFaces();
7806
7807               vector<const SMDS_MeshNode *> poly_nodes;
7808               vector<int> quantities;
7809
7810               for (int iface = 1; iface <= nbFaces; iface++) {
7811                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7812                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7813
7814                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7815                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7816                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7817                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7818                     faceNode = (*nnIt).second;
7819                   }
7820                   faceNodes[inode - 1] = faceNode;
7821                 }
7822
7823                 SimplifyFace(faceNodes, poly_nodes, quantities);
7824               }
7825
7826               if (quantities.size() > 3) {
7827                 // to be done: remove coincident faces
7828               }
7829
7830               if (quantities.size() > 3)
7831                 {
7832                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7833                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7834                   const SMDS_MeshElement* newElem = 0;
7835                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7836                   myLastCreatedElems.Append(newElem);
7837                   if ( aShapeId && newElem )
7838                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7839                   rmElemIds.push_back(elem->GetID());
7840                 }
7841             }
7842             else {
7843               rmElemIds.push_back(elem->GetID());
7844             }
7845           }
7846         }
7847         else {
7848         }
7849
7850         continue;
7851       } // poly element
7852
7853       // Regular elements
7854       // TODO not all the possible cases are solved. Find something more generic?
7855       switch ( nbNodes ) {
7856       case 2: ///////////////////////////////////// EDGE
7857         isOk = false; break;
7858       case 3: ///////////////////////////////////// TRIANGLE
7859         isOk = false; break;
7860       case 4:
7861         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7862           isOk = false;
7863         else { //////////////////////////////////// QUADRANGLE
7864           if ( nbUniqueNodes < 3 )
7865             isOk = false;
7866           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7867             isOk = false; // opposite nodes stick
7868           //MESSAGE("isOk " << isOk);
7869         }
7870         break;
7871       case 6: ///////////////////////////////////// PENTAHEDRON
7872         if ( nbUniqueNodes == 4 ) {
7873           // ---------------------------------> tetrahedron
7874           if (nbRepl == 3 &&
7875               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7876             // all top nodes stick: reverse a bottom
7877             uniqueNodes[ 0 ] = curNodes [ 1 ];
7878             uniqueNodes[ 1 ] = curNodes [ 0 ];
7879           }
7880           else if (nbRepl == 3 &&
7881                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7882             // all bottom nodes stick: set a top before
7883             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7884             uniqueNodes[ 0 ] = curNodes [ 3 ];
7885             uniqueNodes[ 1 ] = curNodes [ 4 ];
7886             uniqueNodes[ 2 ] = curNodes [ 5 ];
7887           }
7888           else if (nbRepl == 4 &&
7889                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7890             // a lateral face turns into a line: reverse a bottom
7891             uniqueNodes[ 0 ] = curNodes [ 1 ];
7892             uniqueNodes[ 1 ] = curNodes [ 0 ];
7893           }
7894           else
7895             isOk = false;
7896         }
7897         else if ( nbUniqueNodes == 5 ) {
7898           // PENTAHEDRON --------------------> 2 tetrahedrons
7899           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7900             // a bottom node sticks with a linked top one
7901             // 1.
7902             SMDS_MeshElement* newElem =
7903               aMesh->AddVolume(curNodes[ 3 ],
7904                                curNodes[ 4 ],
7905                                curNodes[ 5 ],
7906                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7907             myLastCreatedElems.Append(newElem);
7908             if ( aShapeId )
7909               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7910             // 2. : reverse a bottom
7911             uniqueNodes[ 0 ] = curNodes [ 1 ];
7912             uniqueNodes[ 1 ] = curNodes [ 0 ];
7913             nbUniqueNodes = 4;
7914           }
7915           else
7916             isOk = false;
7917         }
7918         else
7919           isOk = false;
7920         break;
7921       case 8: {
7922         if(elem->IsQuadratic()) { // Quadratic quadrangle
7923           //   1    5    2
7924           //    +---+---+
7925           //    |       |
7926           //    |       |
7927           //   4+       +6
7928           //    |       |
7929           //    |       |
7930           //    +---+---+
7931           //   0    7    3
7932           isOk = false;
7933           if(nbRepl==2) {
7934             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7935           }
7936           if(nbRepl==3) {
7937             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7938             nbUniqueNodes = 6;
7939             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7940               uniqueNodes[0] = curNodes[0];
7941               uniqueNodes[1] = curNodes[2];
7942               uniqueNodes[2] = curNodes[3];
7943               uniqueNodes[3] = curNodes[5];
7944               uniqueNodes[4] = curNodes[6];
7945               uniqueNodes[5] = curNodes[7];
7946               isOk = true;
7947             }
7948             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7949               uniqueNodes[0] = curNodes[0];
7950               uniqueNodes[1] = curNodes[1];
7951               uniqueNodes[2] = curNodes[2];
7952               uniqueNodes[3] = curNodes[4];
7953               uniqueNodes[4] = curNodes[5];
7954               uniqueNodes[5] = curNodes[6];
7955               isOk = true;
7956             }
7957             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7958               uniqueNodes[0] = curNodes[1];
7959               uniqueNodes[1] = curNodes[2];
7960               uniqueNodes[2] = curNodes[3];
7961               uniqueNodes[3] = curNodes[5];
7962               uniqueNodes[4] = curNodes[6];
7963               uniqueNodes[5] = curNodes[0];
7964               isOk = true;
7965             }
7966             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7967               uniqueNodes[0] = curNodes[0];
7968               uniqueNodes[1] = curNodes[1];
7969               uniqueNodes[2] = curNodes[3];
7970               uniqueNodes[3] = curNodes[4];
7971               uniqueNodes[4] = curNodes[6];
7972               uniqueNodes[5] = curNodes[7];
7973               isOk = true;
7974             }
7975             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7976               uniqueNodes[0] = curNodes[0];
7977               uniqueNodes[1] = curNodes[2];
7978               uniqueNodes[2] = curNodes[3];
7979               uniqueNodes[3] = curNodes[1];
7980               uniqueNodes[4] = curNodes[6];
7981               uniqueNodes[5] = curNodes[7];
7982               isOk = true;
7983             }
7984             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7985               uniqueNodes[0] = curNodes[0];
7986               uniqueNodes[1] = curNodes[1];
7987               uniqueNodes[2] = curNodes[2];
7988               uniqueNodes[3] = curNodes[4];
7989               uniqueNodes[4] = curNodes[5];
7990               uniqueNodes[5] = curNodes[7];
7991               isOk = true;
7992             }
7993             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7994               uniqueNodes[0] = curNodes[0];
7995               uniqueNodes[1] = curNodes[1];
7996               uniqueNodes[2] = curNodes[3];
7997               uniqueNodes[3] = curNodes[4];
7998               uniqueNodes[4] = curNodes[2];
7999               uniqueNodes[5] = curNodes[7];
8000               isOk = true;
8001             }
8002             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
8003               uniqueNodes[0] = curNodes[0];
8004               uniqueNodes[1] = curNodes[1];
8005               uniqueNodes[2] = curNodes[2];
8006               uniqueNodes[3] = curNodes[4];
8007               uniqueNodes[4] = curNodes[5];
8008               uniqueNodes[5] = curNodes[3];
8009               isOk = true;
8010             }
8011           }
8012           if(nbRepl==4) {
8013             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
8014           }
8015           if(nbRepl==5) {
8016             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
8017           }
8018           break;
8019         }
8020         //////////////////////////////////// HEXAHEDRON
8021         isOk = false;
8022         SMDS_VolumeTool hexa (elem);
8023         hexa.SetExternalNormal();
8024         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8025           //////////////////////// HEX ---> 1 tetrahedron
8026           for ( int iFace = 0; iFace < 6; iFace++ ) {
8027             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8028             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8029                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8030                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8031               // one face turns into a point ...
8032               int iOppFace = hexa.GetOppFaceIndex( iFace );
8033               ind = hexa.GetFaceNodesIndices( iOppFace );
8034               int nbStick = 0;
8035               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8036                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8037                   nbStick++;
8038               }
8039               if ( nbStick == 1 ) {
8040                 // ... and the opposite one - into a triangle.
8041                 // set a top node
8042                 ind = hexa.GetFaceNodesIndices( iFace );
8043                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8044                 isOk = true;
8045               }
8046               break;
8047             }
8048           }
8049         }
8050         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8051           //////////////////////// HEX ---> 1 prism
8052           int nbTria = 0, iTria[3];
8053           const int *ind; // indices of face nodes
8054           // look for triangular faces
8055           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8056             ind = hexa.GetFaceNodesIndices( iFace );
8057             TIDSortedNodeSet faceNodes;
8058             for ( iCur = 0; iCur < 4; iCur++ )
8059               faceNodes.insert( curNodes[ind[iCur]] );
8060             if ( faceNodes.size() == 3 )
8061               iTria[ nbTria++ ] = iFace;
8062           }
8063           // check if triangles are opposite
8064           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8065           {
8066             isOk = true;
8067             // set nodes of the bottom triangle
8068             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8069             vector<int> indB;
8070             for ( iCur = 0; iCur < 4; iCur++ )
8071               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8072                 indB.push_back( ind[iCur] );
8073             if ( !hexa.IsForward() )
8074               std::swap( indB[0], indB[2] );
8075             for ( iCur = 0; iCur < 3; iCur++ )
8076               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8077             // set nodes of the top triangle
8078             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8079             for ( iCur = 0; iCur < 3; ++iCur )
8080               for ( int j = 0; j < 4; ++j )
8081                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8082                 {
8083                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8084                   break;
8085                 }
8086           }
8087           break;
8088         }
8089         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8090           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8091           for ( int iFace = 0; iFace < 6; iFace++ ) {
8092             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8093             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8094                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8095                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8096               // one face turns into a point ...
8097               int iOppFace = hexa.GetOppFaceIndex( iFace );
8098               ind = hexa.GetFaceNodesIndices( iOppFace );
8099               int nbStick = 0;
8100               iUnique = 2;  // reverse a tetrahedron 1 bottom
8101               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8102                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8103                   nbStick++;
8104                 else if ( iUnique >= 0 )
8105                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8106               }
8107               if ( nbStick == 0 ) {
8108                 // ... and the opposite one is a quadrangle
8109                 // set a top node
8110                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8111                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8112                 nbUniqueNodes = 4;
8113                 // tetrahedron 2
8114                 SMDS_MeshElement* newElem =
8115                   aMesh->AddVolume(curNodes[ind[ 0 ]],
8116                                    curNodes[ind[ 3 ]],
8117                                    curNodes[ind[ 2 ]],
8118                                    curNodes[indTop[ 0 ]]);
8119                 myLastCreatedElems.Append(newElem);
8120                 if ( aShapeId )
8121                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8122                 isOk = true;
8123               }
8124               break;
8125             }
8126           }
8127         }
8128         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8129           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8130           // find indices of quad and tri faces
8131           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8132           for ( iFace = 0; iFace < 6; iFace++ ) {
8133             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8134             nodeSet.clear();
8135             for ( iCur = 0; iCur < 4; iCur++ )
8136               nodeSet.insert( curNodes[ind[ iCur ]] );
8137             nbUniqueNodes = nodeSet.size();
8138             if ( nbUniqueNodes == 3 )
8139               iTriFace[ nbTri++ ] = iFace;
8140             else if ( nbUniqueNodes == 4 )
8141               iQuadFace[ nbQuad++ ] = iFace;
8142           }
8143           if (nbQuad == 2 && nbTri == 4 &&
8144               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8145             // 2 opposite quadrangles stuck with a diagonal;
8146             // sample groups of merged indices: (0-4)(2-6)
8147             // --------------------------------------------> 2 tetrahedrons
8148             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8149             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8150             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8151             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8152                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8153               // stuck with 0-2 diagonal
8154               i0  = ind1[ 3 ];
8155               i1d = ind1[ 0 ];
8156               i2  = ind1[ 1 ];
8157               i3d = ind1[ 2 ];
8158               i0t = ind2[ 1 ];
8159               i2t = ind2[ 3 ];
8160             }
8161             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8162                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8163               // stuck with 1-3 diagonal
8164               i0  = ind1[ 0 ];
8165               i1d = ind1[ 1 ];
8166               i2  = ind1[ 2 ];
8167               i3d = ind1[ 3 ];
8168               i0t = ind2[ 0 ];
8169               i2t = ind2[ 1 ];
8170             }
8171             else {
8172               ASSERT(0);
8173             }
8174             // tetrahedron 1
8175             uniqueNodes[ 0 ] = curNodes [ i0 ];
8176             uniqueNodes[ 1 ] = curNodes [ i1d ];
8177             uniqueNodes[ 2 ] = curNodes [ i3d ];
8178             uniqueNodes[ 3 ] = curNodes [ i0t ];
8179             nbUniqueNodes = 4;
8180             // tetrahedron 2
8181             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8182                                                          curNodes[ i2 ],
8183                                                          curNodes[ i3d ],
8184                                                          curNodes[ i2t ]);
8185             myLastCreatedElems.Append(newElem);
8186             if ( aShapeId )
8187               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8188             isOk = true;
8189           }
8190           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8191                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8192             // --------------------------------------------> prism
8193             // find 2 opposite triangles
8194             nbUniqueNodes = 6;
8195             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8196               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8197                 // find indices of kept and replaced nodes
8198                 // and fill unique nodes of 2 opposite triangles
8199                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8200                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8201                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8202                 // fill unique nodes
8203                 iUnique = 0;
8204                 isOk = true;
8205                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8206                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8207                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8208                   if ( n == nInit ) {
8209                     // iCur of a linked node of the opposite face (make normals co-directed):
8210                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8211                     // check that correspondent corners of triangles are linked
8212                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8213                       isOk = false;
8214                     else {
8215                       uniqueNodes[ iUnique ] = n;
8216                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8217                       iUnique++;
8218                     }
8219                   }
8220                 }
8221                 break;
8222               }
8223             }
8224           }
8225         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8226         else
8227         {
8228           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8229         }
8230         break;
8231       } // HEXAHEDRON
8232
8233       default:
8234         isOk = false;
8235       } // switch ( nbNodes )
8236
8237     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8238
8239     if ( isOk ) { // the elem remains valid after sticking nodes
8240       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8241       {
8242         // Change nodes of polyedre
8243         const SMDS_VtkVolume* aPolyedre =
8244           dynamic_cast<const SMDS_VtkVolume*>( elem );
8245         if (aPolyedre) {
8246           int nbFaces = aPolyedre->NbFaces();
8247
8248           vector<const SMDS_MeshNode *> poly_nodes;
8249           vector<int> quantities (nbFaces);
8250
8251           for (int iface = 1; iface <= nbFaces; iface++) {
8252             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8253             quantities[iface - 1] = nbFaceNodes;
8254
8255             for (inode = 1; inode <= nbFaceNodes; inode++) {
8256               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8257
8258               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8259               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8260                 curNode = (*nnIt).second;
8261               }
8262               poly_nodes.push_back(curNode);
8263             }
8264           }
8265           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8266         }
8267       }
8268       else // replace non-polyhedron elements
8269       {
8270         const SMDSAbs_ElementType etyp = elem->GetType();
8271         const int elemId               = elem->GetID();
8272         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8273         uniqueNodes.resize(nbUniqueNodes);
8274
8275         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8276
8277         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8278         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8279         if ( sm && newElem )
8280           sm->AddElement( newElem );
8281         if ( elem != newElem )
8282           ReplaceElemInGroups( elem, newElem, aMesh );
8283       }
8284     }
8285     else {
8286       // Remove invalid regular element or invalid polygon
8287       rmElemIds.push_back( elem->GetID() );
8288     }
8289
8290   } // loop on elements
8291
8292   // Remove bad elements, then equal nodes (order important)
8293
8294   Remove( rmElemIds, false );
8295   Remove( rmNodeIds, true );
8296
8297 }
8298
8299
8300 // ========================================================
8301 // class   : SortableElement
8302 // purpose : allow sorting elements basing on their nodes
8303 // ========================================================
8304 class SortableElement : public set <const SMDS_MeshElement*>
8305 {
8306 public:
8307
8308   SortableElement( const SMDS_MeshElement* theElem )
8309   {
8310     myElem = theElem;
8311     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8312     while ( nodeIt->more() )
8313       this->insert( nodeIt->next() );
8314   }
8315
8316   const SMDS_MeshElement* Get() const
8317   { return myElem; }
8318
8319   void Set(const SMDS_MeshElement* e) const
8320   { myElem = e; }
8321
8322
8323 private:
8324   mutable const SMDS_MeshElement* myElem;
8325 };
8326
8327 //=======================================================================
8328 //function : FindEqualElements
8329 //purpose  : Return list of group of elements built on the same nodes.
8330 //           Search among theElements or in the whole mesh if theElements is empty
8331 //=======================================================================
8332
8333 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8334                                          TListOfListOfElementsID & theGroupsOfElementsID)
8335 {
8336   myLastCreatedElems.Clear();
8337   myLastCreatedNodes.Clear();
8338
8339   typedef map< SortableElement, int > TMapOfNodeSet;
8340   typedef list<int> TGroupOfElems;
8341
8342   if ( theElements.empty() )
8343   { // get all elements in the mesh
8344     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8345     while ( eIt->more() )
8346       theElements.insert( theElements.end(), eIt->next());
8347   }
8348
8349   vector< TGroupOfElems > arrayOfGroups;
8350   TGroupOfElems groupOfElems;
8351   TMapOfNodeSet mapOfNodeSet;
8352
8353   TIDSortedElemSet::iterator elemIt = theElements.begin();
8354   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8355     const SMDS_MeshElement* curElem = *elemIt;
8356     SortableElement SE(curElem);
8357     int ind = -1;
8358     // check uniqueness
8359     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8360     if( !(pp.second) ) {
8361       TMapOfNodeSet::iterator& itSE = pp.first;
8362       ind = (*itSE).second;
8363       arrayOfGroups[ind].push_back(curElem->GetID());
8364     }
8365     else {
8366       groupOfElems.clear();
8367       groupOfElems.push_back(curElem->GetID());
8368       arrayOfGroups.push_back(groupOfElems);
8369       i++;
8370     }
8371   }
8372
8373   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8374   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8375     groupOfElems = *groupIt;
8376     if ( groupOfElems.size() > 1 ) {
8377       groupOfElems.sort();
8378       theGroupsOfElementsID.push_back(groupOfElems);
8379     }
8380   }
8381 }
8382
8383 //=======================================================================
8384 //function : MergeElements
8385 //purpose  : In each given group, substitute all elements by the first one.
8386 //=======================================================================
8387
8388 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8389 {
8390   myLastCreatedElems.Clear();
8391   myLastCreatedNodes.Clear();
8392
8393   typedef list<int> TListOfIDs;
8394   TListOfIDs rmElemIds; // IDs of elems to remove
8395
8396   SMESHDS_Mesh* aMesh = GetMeshDS();
8397
8398   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8399   while ( groupsIt != theGroupsOfElementsID.end() ) {
8400     TListOfIDs& aGroupOfElemID = *groupsIt;
8401     aGroupOfElemID.sort();
8402     int elemIDToKeep = aGroupOfElemID.front();
8403     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8404     aGroupOfElemID.pop_front();
8405     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8406     while ( idIt != aGroupOfElemID.end() ) {
8407       int elemIDToRemove = *idIt;
8408       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8409       // add the kept element in groups of removed one (PAL15188)
8410       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8411       rmElemIds.push_back( elemIDToRemove );
8412       ++idIt;
8413     }
8414     ++groupsIt;
8415   }
8416
8417   Remove( rmElemIds, false );
8418 }
8419
8420 //=======================================================================
8421 //function : MergeEqualElements
8422 //purpose  : Remove all but one of elements built on the same nodes.
8423 //=======================================================================
8424
8425 void SMESH_MeshEditor::MergeEqualElements()
8426 {
8427   TIDSortedElemSet aMeshElements; /* empty input ==
8428                                      to merge equal elements in the whole mesh */
8429   TListOfListOfElementsID aGroupsOfElementsID;
8430   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8431   MergeElements(aGroupsOfElementsID);
8432 }
8433
8434 //=======================================================================
8435 //function : FindFaceInSet
8436 //purpose  : Return a face having linked nodes n1 and n2 and which is
8437 //           - not in avoidSet,
8438 //           - in elemSet provided that !elemSet.empty()
8439 //           i1 and i2 optionally returns indices of n1 and n2
8440 //=======================================================================
8441
8442 const SMDS_MeshElement*
8443 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8444                                 const SMDS_MeshNode*    n2,
8445                                 const TIDSortedElemSet& elemSet,
8446                                 const TIDSortedElemSet& avoidSet,
8447                                 int*                    n1ind,
8448                                 int*                    n2ind)
8449
8450 {
8451   int i1, i2;
8452   const SMDS_MeshElement* face = 0;
8453
8454   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8455   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8456   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8457   {
8458     //MESSAGE("in while ( invElemIt->more() && !face )");
8459     const SMDS_MeshElement* elem = invElemIt->next();
8460     if (avoidSet.count( elem ))
8461       continue;
8462     if ( !elemSet.empty() && !elemSet.count( elem ))
8463       continue;
8464     // index of n1
8465     i1 = elem->GetNodeIndex( n1 );
8466     // find a n2 linked to n1
8467     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8468     for ( int di = -1; di < 2 && !face; di += 2 )
8469     {
8470       i2 = (i1+di+nbN) % nbN;
8471       if ( elem->GetNode( i2 ) == n2 )
8472         face = elem;
8473     }
8474     if ( !face && elem->IsQuadratic())
8475     {
8476       // analysis for quadratic elements using all nodes
8477       const SMDS_VtkFace* F =
8478         dynamic_cast<const SMDS_VtkFace*>(elem);
8479       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8480       // use special nodes iterator
8481       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8482       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8483       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8484       {
8485         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8486         if ( n1 == prevN && n2 == n )
8487         {
8488           face = elem;
8489         }
8490         else if ( n2 == prevN && n1 == n )
8491         {
8492           face = elem; swap( i1, i2 );
8493         }
8494         prevN = n;
8495       }
8496     }
8497   }
8498   if ( n1ind ) *n1ind = i1;
8499   if ( n2ind ) *n2ind = i2;
8500   return face;
8501 }
8502
8503 //=======================================================================
8504 //function : findAdjacentFace
8505 //purpose  :
8506 //=======================================================================
8507
8508 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8509                                                 const SMDS_MeshNode* n2,
8510                                                 const SMDS_MeshElement* elem)
8511 {
8512   TIDSortedElemSet elemSet, avoidSet;
8513   if ( elem )
8514     avoidSet.insert ( elem );
8515   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8516 }
8517
8518 //=======================================================================
8519 //function : FindFreeBorder
8520 //purpose  :
8521 //=======================================================================
8522
8523 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8524
8525 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8526                                        const SMDS_MeshNode*             theSecondNode,
8527                                        const SMDS_MeshNode*             theLastNode,
8528                                        list< const SMDS_MeshNode* > &   theNodes,
8529                                        list< const SMDS_MeshElement* >& theFaces)
8530 {
8531   if ( !theFirstNode || !theSecondNode )
8532     return false;
8533   // find border face between theFirstNode and theSecondNode
8534   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8535   if ( !curElem )
8536     return false;
8537
8538   theFaces.push_back( curElem );
8539   theNodes.push_back( theFirstNode );
8540   theNodes.push_back( theSecondNode );
8541
8542   //vector<const SMDS_MeshNode*> nodes;
8543   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8544   TIDSortedElemSet foundElems;
8545   bool needTheLast = ( theLastNode != 0 );
8546
8547   while ( nStart != theLastNode ) {
8548     if ( nStart == theFirstNode )
8549       return !needTheLast;
8550
8551     // find all free border faces sharing form nStart
8552
8553     list< const SMDS_MeshElement* > curElemList;
8554     list< const SMDS_MeshNode* > nStartList;
8555     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8556     while ( invElemIt->more() ) {
8557       const SMDS_MeshElement* e = invElemIt->next();
8558       if ( e == curElem || foundElems.insert( e ).second ) {
8559         // get nodes
8560         int iNode = 0, nbNodes = e->NbNodes();
8561         //const SMDS_MeshNode* nodes[nbNodes+1];
8562         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8563
8564         if(e->IsQuadratic()) {
8565           const SMDS_VtkFace* F =
8566             dynamic_cast<const SMDS_VtkFace*>(e);
8567           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8568           // use special nodes iterator
8569           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8570           while( anIter->more() ) {
8571             nodes[ iNode++ ] = cast2Node(anIter->next());
8572           }
8573         }
8574         else {
8575           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8576           while ( nIt->more() )
8577             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8578         }
8579         nodes[ iNode ] = nodes[ 0 ];
8580         // check 2 links
8581         for ( iNode = 0; iNode < nbNodes; iNode++ )
8582           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8583                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8584               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8585           {
8586             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8587             curElemList.push_back( e );
8588           }
8589       }
8590     }
8591     // analyse the found
8592
8593     int nbNewBorders = curElemList.size();
8594     if ( nbNewBorders == 0 ) {
8595       // no free border furthermore
8596       return !needTheLast;
8597     }
8598     else if ( nbNewBorders == 1 ) {
8599       // one more element found
8600       nIgnore = nStart;
8601       nStart = nStartList.front();
8602       curElem = curElemList.front();
8603       theFaces.push_back( curElem );
8604       theNodes.push_back( nStart );
8605     }
8606     else {
8607       // several continuations found
8608       list< const SMDS_MeshElement* >::iterator curElemIt;
8609       list< const SMDS_MeshNode* >::iterator nStartIt;
8610       // check if one of them reached the last node
8611       if ( needTheLast ) {
8612         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8613              curElemIt!= curElemList.end();
8614              curElemIt++, nStartIt++ )
8615           if ( *nStartIt == theLastNode ) {
8616             theFaces.push_back( *curElemIt );
8617             theNodes.push_back( *nStartIt );
8618             return true;
8619           }
8620       }
8621       // find the best free border by the continuations
8622       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8623       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8624       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8625            curElemIt!= curElemList.end();
8626            curElemIt++, nStartIt++ )
8627       {
8628         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8629         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8630         // find one more free border
8631         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8632           cNL->clear();
8633           cFL->clear();
8634         }
8635         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8636           // choice: clear a worse one
8637           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8638           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8639           contNodes[ iWorse ].clear();
8640           contFaces[ iWorse ].clear();
8641         }
8642       }
8643       if ( contNodes[0].empty() && contNodes[1].empty() )
8644         return false;
8645
8646       // append the best free border
8647       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8648       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8649       theNodes.pop_back(); // remove nIgnore
8650       theNodes.pop_back(); // remove nStart
8651       theFaces.pop_back(); // remove curElem
8652       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8653       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8654       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8655       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8656       return true;
8657
8658     } // several continuations found
8659   } // while ( nStart != theLastNode )
8660
8661   return true;
8662 }
8663
8664 //=======================================================================
8665 //function : CheckFreeBorderNodes
8666 //purpose  : Return true if the tree nodes are on a free border
8667 //=======================================================================
8668
8669 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8670                                             const SMDS_MeshNode* theNode2,
8671                                             const SMDS_MeshNode* theNode3)
8672 {
8673   list< const SMDS_MeshNode* > nodes;
8674   list< const SMDS_MeshElement* > faces;
8675   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8676 }
8677
8678 //=======================================================================
8679 //function : SewFreeBorder
8680 //purpose  :
8681 //=======================================================================
8682
8683 SMESH_MeshEditor::Sew_Error
8684 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8685                                  const SMDS_MeshNode* theBordSecondNode,
8686                                  const SMDS_MeshNode* theBordLastNode,
8687                                  const SMDS_MeshNode* theSideFirstNode,
8688                                  const SMDS_MeshNode* theSideSecondNode,
8689                                  const SMDS_MeshNode* theSideThirdNode,
8690                                  const bool           theSideIsFreeBorder,
8691                                  const bool           toCreatePolygons,
8692                                  const bool           toCreatePolyedrs)
8693 {
8694   myLastCreatedElems.Clear();
8695   myLastCreatedNodes.Clear();
8696
8697   MESSAGE("::SewFreeBorder()");
8698   Sew_Error aResult = SEW_OK;
8699
8700   // ====================================
8701   //    find side nodes and elements
8702   // ====================================
8703
8704   list< const SMDS_MeshNode* > nSide[ 2 ];
8705   list< const SMDS_MeshElement* > eSide[ 2 ];
8706   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8707   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8708
8709   // Free border 1
8710   // --------------
8711   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8712                       nSide[0], eSide[0])) {
8713     MESSAGE(" Free Border 1 not found " );
8714     aResult = SEW_BORDER1_NOT_FOUND;
8715   }
8716   if (theSideIsFreeBorder) {
8717     // Free border 2
8718     // --------------
8719     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8720                         nSide[1], eSide[1])) {
8721       MESSAGE(" Free Border 2 not found " );
8722       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8723     }
8724   }
8725   if ( aResult != SEW_OK )
8726     return aResult;
8727
8728   if (!theSideIsFreeBorder) {
8729     // Side 2
8730     // --------------
8731
8732     // -------------------------------------------------------------------------
8733     // Algo:
8734     // 1. If nodes to merge are not coincident, move nodes of the free border
8735     //    from the coord sys defined by the direction from the first to last
8736     //    nodes of the border to the correspondent sys of the side 2
8737     // 2. On the side 2, find the links most co-directed with the correspondent
8738     //    links of the free border
8739     // -------------------------------------------------------------------------
8740
8741     // 1. Since sewing may break if there are volumes to split on the side 2,
8742     //    we wont move nodes but just compute new coordinates for them
8743     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8744     TNodeXYZMap nBordXYZ;
8745     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8746     list< const SMDS_MeshNode* >::iterator nBordIt;
8747
8748     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8749     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8750     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8751     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8752     double tol2 = 1.e-8;
8753     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8754     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8755       // Need node movement.
8756
8757       // find X and Z axes to create trsf
8758       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8759       gp_Vec X = Zs ^ Zb;
8760       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8761         // Zb || Zs
8762         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8763
8764       // coord systems
8765       gp_Ax3 toBordAx( Pb1, Zb, X );
8766       gp_Ax3 fromSideAx( Ps1, Zs, X );
8767       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8768       // set trsf
8769       gp_Trsf toBordSys, fromSide2Sys;
8770       toBordSys.SetTransformation( toBordAx );
8771       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8772       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8773
8774       // move
8775       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8776         const SMDS_MeshNode* n = *nBordIt;
8777         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8778         toBordSys.Transforms( xyz );
8779         fromSide2Sys.Transforms( xyz );
8780         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8781       }
8782     }
8783     else {
8784       // just insert nodes XYZ in the nBordXYZ map
8785       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8786         const SMDS_MeshNode* n = *nBordIt;
8787         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8788       }
8789     }
8790
8791     // 2. On the side 2, find the links most co-directed with the correspondent
8792     //    links of the free border
8793
8794     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8795     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8796     sideNodes.push_back( theSideFirstNode );
8797
8798     bool hasVolumes = false;
8799     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8800     set<long> foundSideLinkIDs, checkedLinkIDs;
8801     SMDS_VolumeTool volume;
8802     //const SMDS_MeshNode* faceNodes[ 4 ];
8803
8804     const SMDS_MeshNode*    sideNode;
8805     const SMDS_MeshElement* sideElem;
8806     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8807     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8808     nBordIt = bordNodes.begin();
8809     nBordIt++;
8810     // border node position and border link direction to compare with
8811     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8812     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8813     // choose next side node by link direction or by closeness to
8814     // the current border node:
8815     bool searchByDir = ( *nBordIt != theBordLastNode );
8816     do {
8817       // find the next node on the Side 2
8818       sideNode = 0;
8819       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8820       long linkID;
8821       checkedLinkIDs.clear();
8822       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8823
8824       // loop on inverse elements of current node (prevSideNode) on the Side 2
8825       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8826       while ( invElemIt->more() )
8827       {
8828         const SMDS_MeshElement* elem = invElemIt->next();
8829         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8830         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8831         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8832         bool isVolume = volume.Set( elem );
8833         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8834         if ( isVolume ) // --volume
8835           hasVolumes = true;
8836         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8837           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8838           if(elem->IsQuadratic()) {
8839             const SMDS_VtkFace* F =
8840               dynamic_cast<const SMDS_VtkFace*>(elem);
8841             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8842             // use special nodes iterator
8843             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8844             while( anIter->more() ) {
8845               nodes[ iNode ] = cast2Node(anIter->next());
8846               if ( nodes[ iNode++ ] == prevSideNode )
8847                 iPrevNode = iNode - 1;
8848             }
8849           }
8850           else {
8851             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8852             while ( nIt->more() ) {
8853               nodes[ iNode ] = cast2Node( nIt->next() );
8854               if ( nodes[ iNode++ ] == prevSideNode )
8855                 iPrevNode = iNode - 1;
8856             }
8857           }
8858           // there are 2 links to check
8859           nbNodes = 2;
8860         }
8861         else // --edge
8862           continue;
8863         // loop on links, to be precise, on the second node of links
8864         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8865           const SMDS_MeshNode* n = nodes[ iNode ];
8866           if ( isVolume ) {
8867             if ( !volume.IsLinked( n, prevSideNode ))
8868               continue;
8869           }
8870           else {
8871             if ( iNode ) // a node before prevSideNode
8872               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8873             else         // a node after prevSideNode
8874               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8875           }
8876           // check if this link was already used
8877           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8878           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8879           if (!isJustChecked &&
8880               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8881           {
8882             // test a link geometrically
8883             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8884             bool linkIsBetter = false;
8885             double dot = 0.0, dist = 0.0;
8886             if ( searchByDir ) { // choose most co-directed link
8887               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8888               linkIsBetter = ( dot > maxDot );
8889             }
8890             else { // choose link with the node closest to bordPos
8891               dist = ( nextXYZ - bordPos ).SquareModulus();
8892               linkIsBetter = ( dist < minDist );
8893             }
8894             if ( linkIsBetter ) {
8895               maxDot = dot;
8896               minDist = dist;
8897               linkID = iLink;
8898               sideNode = n;
8899               sideElem = elem;
8900             }
8901           }
8902         }
8903       } // loop on inverse elements of prevSideNode
8904
8905       if ( !sideNode ) {
8906         MESSAGE(" Cant find path by links of the Side 2 ");
8907         return SEW_BAD_SIDE_NODES;
8908       }
8909       sideNodes.push_back( sideNode );
8910       sideElems.push_back( sideElem );
8911       foundSideLinkIDs.insert ( linkID );
8912       prevSideNode = sideNode;
8913
8914       if ( *nBordIt == theBordLastNode )
8915         searchByDir = false;
8916       else {
8917         // find the next border link to compare with
8918         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8919         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8920         // move to next border node if sideNode is before forward border node (bordPos)
8921         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8922           prevBordNode = *nBordIt;
8923           nBordIt++;
8924           bordPos = nBordXYZ[ *nBordIt ];
8925           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8926           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8927         }
8928       }
8929     }
8930     while ( sideNode != theSideSecondNode );
8931
8932     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8933       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8934       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8935     }
8936   } // end nodes search on the side 2
8937
8938   // ============================
8939   // sew the border to the side 2
8940   // ============================
8941
8942   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8943   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8944
8945   TListOfListOfNodes nodeGroupsToMerge;
8946   if ( nbNodes[0] == nbNodes[1] ||
8947        ( theSideIsFreeBorder && !theSideThirdNode)) {
8948
8949     // all nodes are to be merged
8950
8951     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8952          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8953          nIt[0]++, nIt[1]++ )
8954     {
8955       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8956       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8957       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8958     }
8959   }
8960   else {
8961
8962     // insert new nodes into the border and the side to get equal nb of segments
8963
8964     // get normalized parameters of nodes on the borders
8965     //double param[ 2 ][ maxNbNodes ];
8966     double* param[ 2 ];
8967     param[0] = new double [ maxNbNodes ];
8968     param[1] = new double [ maxNbNodes ];
8969     int iNode, iBord;
8970     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8971       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8972       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8973       const SMDS_MeshNode* nPrev = *nIt;
8974       double bordLength = 0;
8975       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8976         const SMDS_MeshNode* nCur = *nIt;
8977         gp_XYZ segment (nCur->X() - nPrev->X(),
8978                         nCur->Y() - nPrev->Y(),
8979                         nCur->Z() - nPrev->Z());
8980         double segmentLen = segment.Modulus();
8981         bordLength += segmentLen;
8982         param[ iBord ][ iNode ] = bordLength;
8983         nPrev = nCur;
8984       }
8985       // normalize within [0,1]
8986       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8987         param[ iBord ][ iNode ] /= bordLength;
8988       }
8989     }
8990
8991     // loop on border segments
8992     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8993     int i[ 2 ] = { 0, 0 };
8994     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8995     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8996
8997     TElemOfNodeListMap insertMap;
8998     TElemOfNodeListMap::iterator insertMapIt;
8999     // insertMap is
9000     // key:   elem to insert nodes into
9001     // value: 2 nodes to insert between + nodes to be inserted
9002     do {
9003       bool next[ 2 ] = { false, false };
9004
9005       // find min adjacent segment length after sewing
9006       double nextParam = 10., prevParam = 0;
9007       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9008         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
9009           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
9010         if ( i[ iBord ] > 0 )
9011           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
9012       }
9013       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
9014       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
9015       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
9016
9017       // choose to insert or to merge nodes
9018       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
9019       if ( Abs( du ) <= minSegLen * 0.2 ) {
9020         // merge
9021         // ------
9022         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9023         const SMDS_MeshNode* n0 = *nIt[0];
9024         const SMDS_MeshNode* n1 = *nIt[1];
9025         nodeGroupsToMerge.back().push_back( n1 );
9026         nodeGroupsToMerge.back().push_back( n0 );
9027         // position of node of the border changes due to merge
9028         param[ 0 ][ i[0] ] += du;
9029         // move n1 for the sake of elem shape evaluation during insertion.
9030         // n1 will be removed by MergeNodes() anyway
9031         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9032         next[0] = next[1] = true;
9033       }
9034       else {
9035         // insert
9036         // ------
9037         int intoBord = ( du < 0 ) ? 0 : 1;
9038         const SMDS_MeshElement* elem = *eIt[ intoBord ];
9039         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
9040         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
9041         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
9042         if ( intoBord == 1 ) {
9043           // move node of the border to be on a link of elem of the side
9044           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9045           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9046           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9047           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9048           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9049         }
9050         insertMapIt = insertMap.find( elem );
9051         bool notFound = ( insertMapIt == insertMap.end() );
9052         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9053         if ( otherLink ) {
9054           // insert into another link of the same element:
9055           // 1. perform insertion into the other link of the elem
9056           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9057           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9058           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9059           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9060           // 2. perform insertion into the link of adjacent faces
9061           while (true) {
9062             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9063             if ( adjElem )
9064               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9065             else
9066               break;
9067           }
9068           if (toCreatePolyedrs) {
9069             // perform insertion into the links of adjacent volumes
9070             UpdateVolumes(n12, n22, nodeList);
9071           }
9072           // 3. find an element appeared on n1 and n2 after the insertion
9073           insertMap.erase( elem );
9074           elem = findAdjacentFace( n1, n2, 0 );
9075         }
9076         if ( notFound || otherLink ) {
9077           // add element and nodes of the side into the insertMap
9078           insertMapIt = insertMap.insert
9079             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9080           (*insertMapIt).second.push_back( n1 );
9081           (*insertMapIt).second.push_back( n2 );
9082         }
9083         // add node to be inserted into elem
9084         (*insertMapIt).second.push_back( nIns );
9085         next[ 1 - intoBord ] = true;
9086       }
9087
9088       // go to the next segment
9089       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9090         if ( next[ iBord ] ) {
9091           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9092             eIt[ iBord ]++;
9093           nPrev[ iBord ] = *nIt[ iBord ];
9094           nIt[ iBord ]++; i[ iBord ]++;
9095         }
9096       }
9097     }
9098     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9099
9100     // perform insertion of nodes into elements
9101
9102     for (insertMapIt = insertMap.begin();
9103          insertMapIt != insertMap.end();
9104          insertMapIt++ )
9105     {
9106       const SMDS_MeshElement* elem = (*insertMapIt).first;
9107       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9108       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9109       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9110
9111       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9112
9113       if ( !theSideIsFreeBorder ) {
9114         // look for and insert nodes into the faces adjacent to elem
9115         while (true) {
9116           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9117           if ( adjElem )
9118             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9119           else
9120             break;
9121         }
9122       }
9123       if (toCreatePolyedrs) {
9124         // perform insertion into the links of adjacent volumes
9125         UpdateVolumes(n1, n2, nodeList);
9126       }
9127     }
9128
9129     delete param[0];
9130     delete param[1];
9131   } // end: insert new nodes
9132
9133   MergeNodes ( nodeGroupsToMerge );
9134
9135   return aResult;
9136 }
9137
9138 //=======================================================================
9139 //function : InsertNodesIntoLink
9140 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9141 //           and theBetweenNode2 and split theElement
9142 //=======================================================================
9143
9144 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9145                                            const SMDS_MeshNode*        theBetweenNode1,
9146                                            const SMDS_MeshNode*        theBetweenNode2,
9147                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9148                                            const bool                  toCreatePoly)
9149 {
9150   if ( theFace->GetType() != SMDSAbs_Face ) return;
9151
9152   // find indices of 2 link nodes and of the rest nodes
9153   int iNode = 0, il1, il2, i3, i4;
9154   il1 = il2 = i3 = i4 = -1;
9155   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9156   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9157
9158   if(theFace->IsQuadratic()) {
9159     const SMDS_VtkFace* F =
9160       dynamic_cast<const SMDS_VtkFace*>(theFace);
9161     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9162     // use special nodes iterator
9163     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9164     while( anIter->more() ) {
9165       const SMDS_MeshNode* n = cast2Node(anIter->next());
9166       if ( n == theBetweenNode1 )
9167         il1 = iNode;
9168       else if ( n == theBetweenNode2 )
9169         il2 = iNode;
9170       else if ( i3 < 0 )
9171         i3 = iNode;
9172       else
9173         i4 = iNode;
9174       nodes[ iNode++ ] = n;
9175     }
9176   }
9177   else {
9178     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9179     while ( nodeIt->more() ) {
9180       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9181       if ( n == theBetweenNode1 )
9182         il1 = iNode;
9183       else if ( n == theBetweenNode2 )
9184         il2 = iNode;
9185       else if ( i3 < 0 )
9186         i3 = iNode;
9187       else
9188         i4 = iNode;
9189       nodes[ iNode++ ] = n;
9190     }
9191   }
9192   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9193     return ;
9194
9195   // arrange link nodes to go one after another regarding the face orientation
9196   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9197   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9198   if ( reverse ) {
9199     iNode = il1;
9200     il1 = il2;
9201     il2 = iNode;
9202     aNodesToInsert.reverse();
9203   }
9204   // check that not link nodes of a quadrangles are in good order
9205   int nbFaceNodes = theFace->NbNodes();
9206   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9207     iNode = i3;
9208     i3 = i4;
9209     i4 = iNode;
9210   }
9211
9212   if (toCreatePoly || theFace->IsPoly()) {
9213
9214     iNode = 0;
9215     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9216
9217     // add nodes of face up to first node of link
9218     bool isFLN = false;
9219
9220     if(theFace->IsQuadratic()) {
9221       const SMDS_VtkFace* F =
9222         dynamic_cast<const SMDS_VtkFace*>(theFace);
9223       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9224       // use special nodes iterator
9225       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9226       while( anIter->more()  && !isFLN ) {
9227         const SMDS_MeshNode* n = cast2Node(anIter->next());
9228         poly_nodes[iNode++] = n;
9229         if (n == nodes[il1]) {
9230           isFLN = true;
9231         }
9232       }
9233       // add nodes to insert
9234       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9235       for (; nIt != aNodesToInsert.end(); nIt++) {
9236         poly_nodes[iNode++] = *nIt;
9237       }
9238       // add nodes of face starting from last node of link
9239       while ( anIter->more() ) {
9240         poly_nodes[iNode++] = cast2Node(anIter->next());
9241       }
9242     }
9243     else {
9244       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9245       while ( nodeIt->more() && !isFLN ) {
9246         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9247         poly_nodes[iNode++] = n;
9248         if (n == nodes[il1]) {
9249           isFLN = true;
9250         }
9251       }
9252       // add nodes to insert
9253       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9254       for (; nIt != aNodesToInsert.end(); nIt++) {
9255         poly_nodes[iNode++] = *nIt;
9256       }
9257       // add nodes of face starting from last node of link
9258       while ( nodeIt->more() ) {
9259         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9260         poly_nodes[iNode++] = n;
9261       }
9262     }
9263
9264     // edit or replace the face
9265     SMESHDS_Mesh *aMesh = GetMeshDS();
9266
9267     if (theFace->IsPoly()) {
9268       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9269     }
9270     else {
9271       int aShapeId = FindShape( theFace );
9272
9273       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9274       myLastCreatedElems.Append(newElem);
9275       if ( aShapeId && newElem )
9276         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9277
9278       aMesh->RemoveElement(theFace);
9279     }
9280     return;
9281   }
9282
9283   SMESHDS_Mesh *aMesh = GetMeshDS();
9284   if( !theFace->IsQuadratic() ) {
9285
9286     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9287     int nbLinkNodes = 2 + aNodesToInsert.size();
9288     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9289     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9290     linkNodes[ 0 ] = nodes[ il1 ];
9291     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9292     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9293     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9294       linkNodes[ iNode++ ] = *nIt;
9295     }
9296     // decide how to split a quadrangle: compare possible variants
9297     // and choose which of splits to be a quadrangle
9298     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9299     if ( nbFaceNodes == 3 ) {
9300       iBestQuad = nbSplits;
9301       i4 = i3;
9302     }
9303     else if ( nbFaceNodes == 4 ) {
9304       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9305       double aBestRate = DBL_MAX;
9306       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9307         i1 = 0; i2 = 1;
9308         double aBadRate = 0;
9309         // evaluate elements quality
9310         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9311           if ( iSplit == iQuad ) {
9312             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9313                                    linkNodes[ i2++ ],
9314                                    nodes[ i3 ],
9315                                    nodes[ i4 ]);
9316             aBadRate += getBadRate( &quad, aCrit );
9317           }
9318           else {
9319             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9320                                    linkNodes[ i2++ ],
9321                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9322             aBadRate += getBadRate( &tria, aCrit );
9323           }
9324         }
9325         // choice
9326         if ( aBadRate < aBestRate ) {
9327           iBestQuad = iQuad;
9328           aBestRate = aBadRate;
9329         }
9330       }
9331     }
9332
9333     // create new elements
9334     int aShapeId = FindShape( theFace );
9335
9336     i1 = 0; i2 = 1;
9337     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9338       SMDS_MeshElement* newElem = 0;
9339       if ( iSplit == iBestQuad )
9340         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9341                                   linkNodes[ i2++ ],
9342                                   nodes[ i3 ],
9343                                   nodes[ i4 ]);
9344       else
9345         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9346                                   linkNodes[ i2++ ],
9347                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9348       myLastCreatedElems.Append(newElem);
9349       if ( aShapeId && newElem )
9350         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9351     }
9352
9353     // change nodes of theFace
9354     const SMDS_MeshNode* newNodes[ 4 ];
9355     newNodes[ 0 ] = linkNodes[ i1 ];
9356     newNodes[ 1 ] = linkNodes[ i2 ];
9357     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9358     newNodes[ 3 ] = nodes[ i4 ];
9359     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9360     const SMDS_MeshElement* newElem = 0;
9361     if (iSplit == iBestQuad)
9362       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9363     else
9364       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9365     myLastCreatedElems.Append(newElem);
9366     if ( aShapeId && newElem )
9367       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9368 } // end if(!theFace->IsQuadratic())
9369   else { // theFace is quadratic
9370     // we have to split theFace on simple triangles and one simple quadrangle
9371     int tmp = il1/2;
9372     int nbshift = tmp*2;
9373     // shift nodes in nodes[] by nbshift
9374     int i,j;
9375     for(i=0; i<nbshift; i++) {
9376       const SMDS_MeshNode* n = nodes[0];
9377       for(j=0; j<nbFaceNodes-1; j++) {
9378         nodes[j] = nodes[j+1];
9379       }
9380       nodes[nbFaceNodes-1] = n;
9381     }
9382     il1 = il1 - nbshift;
9383     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9384     //   n0      n1     n2    n0      n1     n2
9385     //     +-----+-----+        +-----+-----+
9386     //      \         /         |           |
9387     //       \       /          |           |
9388     //      n5+     +n3       n7+           +n3
9389     //         \   /            |           |
9390     //          \ /             |           |
9391     //           +              +-----+-----+
9392     //           n4           n6      n5     n4
9393
9394     // create new elements
9395     int aShapeId = FindShape( theFace );
9396
9397     int n1,n2,n3;
9398     if(nbFaceNodes==6) { // quadratic triangle
9399       SMDS_MeshElement* newElem =
9400         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9401       myLastCreatedElems.Append(newElem);
9402       if ( aShapeId && newElem )
9403         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9404       if(theFace->IsMediumNode(nodes[il1])) {
9405         // create quadrangle
9406         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9407         myLastCreatedElems.Append(newElem);
9408         if ( aShapeId && newElem )
9409           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9410         n1 = 1;
9411         n2 = 2;
9412         n3 = 3;
9413       }
9414       else {
9415         // create quadrangle
9416         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9417         myLastCreatedElems.Append(newElem);
9418         if ( aShapeId && newElem )
9419           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9420         n1 = 0;
9421         n2 = 1;
9422         n3 = 5;
9423       }
9424     }
9425     else { // nbFaceNodes==8 - quadratic quadrangle
9426       SMDS_MeshElement* newElem =
9427         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9428       myLastCreatedElems.Append(newElem);
9429       if ( aShapeId && newElem )
9430         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9431       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9432       myLastCreatedElems.Append(newElem);
9433       if ( aShapeId && newElem )
9434         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9435       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9436       myLastCreatedElems.Append(newElem);
9437       if ( aShapeId && newElem )
9438         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9439       if(theFace->IsMediumNode(nodes[il1])) {
9440         // create quadrangle
9441         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9442         myLastCreatedElems.Append(newElem);
9443         if ( aShapeId && newElem )
9444           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9445         n1 = 1;
9446         n2 = 2;
9447         n3 = 3;
9448       }
9449       else {
9450         // create quadrangle
9451         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9452         myLastCreatedElems.Append(newElem);
9453         if ( aShapeId && newElem )
9454           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9455         n1 = 0;
9456         n2 = 1;
9457         n3 = 7;
9458       }
9459     }
9460     // create needed triangles using n1,n2,n3 and inserted nodes
9461     int nbn = 2 + aNodesToInsert.size();
9462     //const SMDS_MeshNode* aNodes[nbn];
9463     vector<const SMDS_MeshNode*> aNodes(nbn);
9464     aNodes[0] = nodes[n1];
9465     aNodes[nbn-1] = nodes[n2];
9466     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9467     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9468       aNodes[iNode++] = *nIt;
9469     }
9470     for(i=1; i<nbn; i++) {
9471       SMDS_MeshElement* newElem =
9472         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9473       myLastCreatedElems.Append(newElem);
9474       if ( aShapeId && newElem )
9475         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9476     }
9477   }
9478   // remove old face
9479   aMesh->RemoveElement(theFace);
9480 }
9481
9482 //=======================================================================
9483 //function : UpdateVolumes
9484 //purpose  :
9485 //=======================================================================
9486 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9487                                       const SMDS_MeshNode*        theBetweenNode2,
9488                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9489 {
9490   myLastCreatedElems.Clear();
9491   myLastCreatedNodes.Clear();
9492
9493   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9494   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9495     const SMDS_MeshElement* elem = invElemIt->next();
9496
9497     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9498     SMDS_VolumeTool aVolume (elem);
9499     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9500       continue;
9501
9502     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9503     int iface, nbFaces = aVolume.NbFaces();
9504     vector<const SMDS_MeshNode *> poly_nodes;
9505     vector<int> quantities (nbFaces);
9506
9507     for (iface = 0; iface < nbFaces; iface++) {
9508       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9509       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9510       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9511
9512       for (int inode = 0; inode < nbFaceNodes; inode++) {
9513         poly_nodes.push_back(faceNodes[inode]);
9514
9515         if (nbInserted == 0) {
9516           if (faceNodes[inode] == theBetweenNode1) {
9517             if (faceNodes[inode + 1] == theBetweenNode2) {
9518               nbInserted = theNodesToInsert.size();
9519
9520               // add nodes to insert
9521               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9522               for (; nIt != theNodesToInsert.end(); nIt++) {
9523                 poly_nodes.push_back(*nIt);
9524               }
9525             }
9526           }
9527           else if (faceNodes[inode] == theBetweenNode2) {
9528             if (faceNodes[inode + 1] == theBetweenNode1) {
9529               nbInserted = theNodesToInsert.size();
9530
9531               // add nodes to insert in reversed order
9532               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9533               nIt--;
9534               for (; nIt != theNodesToInsert.begin(); nIt--) {
9535                 poly_nodes.push_back(*nIt);
9536               }
9537               poly_nodes.push_back(*nIt);
9538             }
9539           }
9540           else {
9541           }
9542         }
9543       }
9544       quantities[iface] = nbFaceNodes + nbInserted;
9545     }
9546
9547     // Replace or update the volume
9548     SMESHDS_Mesh *aMesh = GetMeshDS();
9549
9550     if (elem->IsPoly()) {
9551       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9552
9553     }
9554     else {
9555       int aShapeId = FindShape( elem );
9556
9557       SMDS_MeshElement* newElem =
9558         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9559       myLastCreatedElems.Append(newElem);
9560       if (aShapeId && newElem)
9561         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9562
9563       aMesh->RemoveElement(elem);
9564     }
9565   }
9566 }
9567
9568 namespace
9569 {
9570   //================================================================================
9571   /*!
9572    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9573    */
9574   //================================================================================
9575
9576   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9577                            vector<const SMDS_MeshNode *> & nodes,
9578                            vector<int> &                   nbNodeInFaces )
9579   {
9580     nodes.clear();
9581     nbNodeInFaces.clear();
9582     SMDS_VolumeTool vTool ( elem );
9583     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9584     {
9585       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9586       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9587       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9588     }
9589   }
9590 }
9591
9592 //=======================================================================
9593 /*!
9594  * \brief Convert elements contained in a submesh to quadratic
9595  * \return int - nb of checked elements
9596  */
9597 //=======================================================================
9598
9599 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9600                                              SMESH_MesherHelper& theHelper,
9601                                              const bool          theForce3d)
9602 {
9603   int nbElem = 0;
9604   if( !theSm ) return nbElem;
9605
9606   vector<int> nbNodeInFaces;
9607   vector<const SMDS_MeshNode *> nodes;
9608   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9609   while(ElemItr->more())
9610   {
9611     nbElem++;
9612     const SMDS_MeshElement* elem = ElemItr->next();
9613     if( !elem ) continue;
9614
9615     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9616     if ( elem->IsQuadratic() )
9617     {
9618       bool alreadyOK;
9619       switch ( aGeomType ) {
9620       case SMDSEntity_Quad_Quadrangle:
9621       case SMDSEntity_Quad_Hexa:         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9622       case SMDSEntity_BiQuad_Quadrangle:
9623       case SMDSEntity_TriQuad_Hexa:      alreadyOK = theHelper.GetIsBiQuadratic(); break;
9624       default:                           alreadyOK = true;
9625       }
9626       if ( alreadyOK ) continue;
9627     }
9628     // get elem data needed to re-create it
9629     //
9630     const int id                     = elem->GetID();
9631     const int nbNodes                = elem->NbCornerNodes();
9632     const SMDSAbs_ElementType aType  = elem->GetType();
9633     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9634     if ( aGeomType == SMDSEntity_Polyhedra )
9635       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9636     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9637       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9638
9639     // remove a linear element
9640     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9641
9642     const SMDS_MeshElement* NewElem = 0;
9643
9644     switch( aType )
9645     {
9646     case SMDSAbs_Edge :
9647       {
9648         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9649         break;
9650       }
9651     case SMDSAbs_Face :
9652       {
9653         switch(nbNodes)
9654         {
9655         case 3:
9656           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9657           break;
9658         case 4:
9659           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9660           break;
9661         default:
9662           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9663           continue;
9664         }
9665         break;
9666       }
9667     case SMDSAbs_Volume :
9668       {
9669         switch( aGeomType )
9670         {
9671         case SMDSEntity_Tetra:
9672           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9673           break;
9674         case SMDSEntity_Pyramid:
9675           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9676           break;
9677         case SMDSEntity_Penta:
9678           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9679           break;
9680         case SMDSEntity_Hexa:
9681         case SMDSEntity_Quad_Hexa:
9682         case SMDSEntity_TriQuad_Hexa:
9683           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9684                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9685           break;
9686         case SMDSEntity_Hexagonal_Prism:
9687         default:
9688           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9689         }
9690         break;
9691       }
9692     default :
9693       continue;
9694     }
9695     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9696     if( NewElem )
9697       theSm->AddElement( NewElem );
9698   }
9699   return nbElem;
9700 }
9701 //=======================================================================
9702 //function : ConvertToQuadratic
9703 //purpose  :
9704 //=======================================================================
9705
9706 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9707 {
9708   SMESHDS_Mesh* meshDS = GetMeshDS();
9709
9710   SMESH_MesherHelper aHelper(*myMesh);
9711
9712   aHelper.SetIsQuadratic( true );
9713   aHelper.SetIsBiQuadratic( theToBiQuad );
9714   aHelper.SetElementsOnShape(true);
9715
9716   int nbCheckedElems = 0;
9717   if ( myMesh->HasShapeToMesh() )
9718   {
9719     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9720     {
9721       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9722       while ( smIt->more() ) {
9723         SMESH_subMesh* sm = smIt->next();
9724         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9725           aHelper.SetSubShape( sm->GetSubShape() );
9726           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9727         }
9728       }
9729     }
9730   }
9731   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9732   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9733   {
9734     aHelper.SetElementsOnShape(false);
9735     SMESHDS_SubMesh *smDS = 0;
9736     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9737     while(aEdgeItr->more())
9738     {
9739       const SMDS_MeshEdge* edge = aEdgeItr->next();
9740       if(edge && !edge->IsQuadratic())
9741       {
9742         int id = edge->GetID();
9743         //MESSAGE("edge->GetID() " << id);
9744         const SMDS_MeshNode* n1 = edge->GetNode(0);
9745         const SMDS_MeshNode* n2 = edge->GetNode(1);
9746
9747         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9748
9749         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9750         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9751       }
9752     }
9753     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9754     while(aFaceItr->more())
9755     {
9756       const SMDS_MeshFace* face = aFaceItr->next();
9757       if ( !face ) continue;
9758       
9759       const SMDSAbs_EntityType type = face->GetEntityType();
9760       if (( theToBiQuad  && type == SMDSEntity_BiQuad_Quadrangle ) ||
9761           ( !theToBiQuad && type == SMDSEntity_Quad_Quadrangle ))
9762         continue;
9763
9764       const int id = face->GetID();
9765       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9766
9767       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9768
9769       SMDS_MeshFace * NewFace = 0;
9770       switch( type )
9771       {
9772       case SMDSEntity_Triangle:
9773         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9774         break;
9775       case SMDSEntity_Quadrangle:
9776         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9777         break;
9778       default:
9779         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9780       }
9781       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9782     }
9783     vector<int> nbNodeInFaces;
9784     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9785     while(aVolumeItr->more())
9786     {
9787       const SMDS_MeshVolume* volume = aVolumeItr->next();
9788       if(!volume || volume->IsQuadratic() ) continue;
9789
9790       const SMDSAbs_EntityType type = volume->GetEntityType();
9791       if (( theToBiQuad  && type == SMDSEntity_TriQuad_Hexa ) ||
9792           ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
9793         continue;
9794
9795       const int id = volume->GetID();
9796       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9797       if ( type == SMDSEntity_Polyhedra )
9798         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9799       else if ( type == SMDSEntity_Hexagonal_Prism )
9800         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9801
9802       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9803
9804       SMDS_MeshVolume * NewVolume = 0;
9805       switch ( type )
9806       {
9807       case SMDSEntity_Tetra:
9808         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9809         break;
9810       case SMDSEntity_Hexa:
9811       case SMDSEntity_Quad_Hexa:
9812       case SMDSEntity_TriQuad_Hexa:
9813         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9814                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9815         break;
9816       case SMDSEntity_Pyramid:
9817         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9818                                       nodes[3], nodes[4], id, theForce3d);
9819         break;
9820       case SMDSEntity_Penta:
9821         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9822                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9823         break;
9824       case SMDSEntity_Hexagonal_Prism:
9825       default:
9826         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9827       }
9828       ReplaceElemInGroups(volume, NewVolume, meshDS);
9829     }
9830   }
9831
9832   if ( !theForce3d )
9833   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9834     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9835     aHelper.FixQuadraticElements(myError);
9836   }
9837 }
9838
9839 //================================================================================
9840 /*!
9841  * \brief Makes given elements quadratic
9842  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9843  *  \param theElements - elements to make quadratic
9844  */
9845 //================================================================================
9846
9847 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9848                                           TIDSortedElemSet& theElements,
9849                                           const bool        theToBiQuad)
9850 {
9851   if ( theElements.empty() ) return;
9852
9853   // we believe that all theElements are of the same type
9854   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9855
9856   // get all nodes shared by theElements
9857   TIDSortedNodeSet allNodes;
9858   TIDSortedElemSet::iterator eIt = theElements.begin();
9859   for ( ; eIt != theElements.end(); ++eIt )
9860     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9861
9862   // complete theElements with elements of lower dim whose all nodes are in allNodes
9863
9864   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9865   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9866   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9867   for ( ; nIt != allNodes.end(); ++nIt )
9868   {
9869     const SMDS_MeshNode* n = *nIt;
9870     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9871     while ( invIt->more() )
9872     {
9873       const SMDS_MeshElement* e = invIt->next();
9874       if ( e->IsQuadratic() )
9875       {
9876         bool alreadyOK;
9877         switch ( e->GetEntityType() ) {
9878         case SMDSEntity_Quad_Quadrangle:
9879         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9880         case SMDSEntity_BiQuad_Quadrangle:
9881         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9882         default:                           alreadyOK = true;
9883         }
9884         if ( alreadyOK )
9885         {
9886           quadAdjacentElems[ e->GetType() ].insert( e );
9887           continue;
9888         }
9889       }
9890       if ( e->GetType() >= elemType )
9891       {
9892         continue; // same type of more complex linear element
9893       }
9894
9895       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9896         continue; // e is already checked
9897
9898       // check nodes
9899       bool allIn = true;
9900       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9901       while ( nodeIt->more() && allIn )
9902         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9903       if ( allIn )
9904         theElements.insert(e );
9905     }
9906   }
9907
9908   SMESH_MesherHelper helper(*myMesh);
9909   helper.SetIsQuadratic( true );
9910   helper.SetIsBiQuadratic( theToBiQuad );
9911
9912   // add links of quadratic adjacent elements to the helper
9913
9914   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9915     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9916           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9917     {
9918       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9919     }
9920   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9921     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9922           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9923     {
9924       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9925     }
9926   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9927     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9928           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9929     {
9930       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9931     }
9932
9933   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9934
9935   SMESHDS_Mesh*  meshDS = GetMeshDS();
9936   SMESHDS_SubMesh* smDS = 0;
9937   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9938   {
9939     const SMDS_MeshElement* elem = *eIt;
9940     if( elem->NbNodes() < 2 || elem->IsPoly() )
9941       continue;
9942
9943     if ( elem->IsQuadratic() )
9944     {
9945       bool alreadyOK;
9946       switch ( elem->GetEntityType() ) {
9947       case SMDSEntity_Quad_Quadrangle:
9948       case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9949       case SMDSEntity_BiQuad_Quadrangle:
9950       case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9951       default:                           alreadyOK = true;
9952       }
9953       if ( alreadyOK ) continue;
9954     }
9955
9956     const SMDSAbs_ElementType type = elem->GetType();
9957     const int                   id = elem->GetID();
9958     const int              nbNodes = elem->NbCornerNodes();
9959     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9960
9961     if ( !smDS || !smDS->Contains( elem ))
9962       smDS = meshDS->MeshElements( elem->getshapeId() );
9963     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9964
9965     SMDS_MeshElement * newElem = 0;
9966     switch( nbNodes )
9967     {
9968     case 4: // cases for most frequently used element types go first (for optimization)
9969       if ( type == SMDSAbs_Volume )
9970         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9971       else
9972         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9973       break;
9974     case 8:
9975       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9976                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9977       break;
9978     case 3:
9979       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9980       break;
9981     case 2:
9982       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9983       break;
9984     case 5:
9985       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9986                                  nodes[4], id, theForce3d);
9987       break;
9988     case 6:
9989       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9990                                  nodes[4], nodes[5], id, theForce3d);
9991       break;
9992     default:;
9993     }
9994     ReplaceElemInGroups( elem, newElem, meshDS);
9995     if( newElem && smDS )
9996       smDS->AddElement( newElem );
9997   }
9998
9999   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
10000   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
10001     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
10002     helper.FixQuadraticElements( myError );
10003   }
10004 }
10005
10006 //=======================================================================
10007 /*!
10008  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
10009  * \return int - nb of checked elements
10010  */
10011 //=======================================================================
10012
10013 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
10014                                      SMDS_ElemIteratorPtr theItr,
10015                                      const int            theShapeID)
10016 {
10017   int nbElem = 0;
10018   SMESHDS_Mesh* meshDS = GetMeshDS();
10019
10020   while( theItr->more() )
10021   {
10022     const SMDS_MeshElement* elem = theItr->next();
10023     nbElem++;
10024     if( elem && elem->IsQuadratic())
10025     {
10026       int id                    = elem->GetID();
10027       int nbCornerNodes         = elem->NbCornerNodes();
10028       SMDSAbs_ElementType aType = elem->GetType();
10029
10030       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
10031
10032       //remove a quadratic element
10033       if ( !theSm || !theSm->Contains( elem ))
10034         theSm = meshDS->MeshElements( elem->getshapeId() );
10035       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
10036
10037       // remove medium nodes
10038       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
10039         if ( nodes[i]->NbInverseElements() == 0 )
10040           meshDS->RemoveFreeNode( nodes[i], theSm );
10041
10042       // add a linear element
10043       nodes.resize( nbCornerNodes );
10044       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
10045       ReplaceElemInGroups(elem, newElem, meshDS);
10046       if( theSm && newElem )
10047         theSm->AddElement( newElem );
10048     }
10049   }
10050   return nbElem;
10051 }
10052
10053 //=======================================================================
10054 //function : ConvertFromQuadratic
10055 //purpose  :
10056 //=======================================================================
10057
10058 bool SMESH_MeshEditor::ConvertFromQuadratic()
10059 {
10060   int nbCheckedElems = 0;
10061   if ( myMesh->HasShapeToMesh() )
10062   {
10063     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
10064     {
10065       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
10066       while ( smIt->more() ) {
10067         SMESH_subMesh* sm = smIt->next();
10068         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
10069           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
10070       }
10071     }
10072   }
10073
10074   int totalNbElems =
10075     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
10076   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10077   {
10078     SMESHDS_SubMesh *aSM = 0;
10079     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10080   }
10081
10082   return true;
10083 }
10084
10085 namespace
10086 {
10087   //================================================================================
10088   /*!
10089    * \brief Return true if all medium nodes of the element are in the node set
10090    */
10091   //================================================================================
10092
10093   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10094   {
10095     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10096       if ( !nodeSet.count( elem->GetNode(i) ))
10097         return false;
10098     return true;
10099   }
10100 }
10101
10102 //================================================================================
10103 /*!
10104  * \brief Makes given elements linear
10105  */
10106 //================================================================================
10107
10108 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10109 {
10110   if ( theElements.empty() ) return;
10111
10112   // collect IDs of medium nodes of theElements; some of these nodes will be removed
10113   set<int> mediumNodeIDs;
10114   TIDSortedElemSet::iterator eIt = theElements.begin();
10115   for ( ; eIt != theElements.end(); ++eIt )
10116   {
10117     const SMDS_MeshElement* e = *eIt;
10118     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10119       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10120   }
10121
10122   // replace given elements by linear ones
10123   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10124   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10125   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10126
10127   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10128   // except those elements sharing medium nodes of quadratic element whose medium nodes
10129   // are not all in mediumNodeIDs
10130
10131   // get remaining medium nodes
10132   TIDSortedNodeSet mediumNodes;
10133   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10134   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10135     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10136       mediumNodes.insert( mediumNodes.end(), n );
10137
10138   // find more quadratic elements to convert
10139   TIDSortedElemSet moreElemsToConvert;
10140   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10141   for ( ; nIt != mediumNodes.end(); ++nIt )
10142   {
10143     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10144     while ( invIt->more() )
10145     {
10146       const SMDS_MeshElement* e = invIt->next();
10147       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10148       {
10149         // find a more complex element including e and
10150         // whose medium nodes are not in mediumNodes
10151         bool complexFound = false;
10152         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10153         {
10154           SMDS_ElemIteratorPtr invIt2 =
10155             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10156           while ( invIt2->more() )
10157           {
10158             const SMDS_MeshElement* eComplex = invIt2->next();
10159             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10160             {
10161               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10162               if ( nbCommonNodes == e->NbNodes())
10163               {
10164                 complexFound = true;
10165                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10166                 break;
10167               }
10168             }
10169           }
10170         }
10171         if ( !complexFound )
10172           moreElemsToConvert.insert( e );
10173       }
10174     }
10175   }
10176   elemIt = SMDS_ElemIteratorPtr
10177     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10178   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10179 }
10180
10181 //=======================================================================
10182 //function : SewSideElements
10183 //purpose  :
10184 //=======================================================================
10185
10186 SMESH_MeshEditor::Sew_Error
10187 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10188                                    TIDSortedElemSet&    theSide2,
10189                                    const SMDS_MeshNode* theFirstNode1,
10190                                    const SMDS_MeshNode* theFirstNode2,
10191                                    const SMDS_MeshNode* theSecondNode1,
10192                                    const SMDS_MeshNode* theSecondNode2)
10193 {
10194   myLastCreatedElems.Clear();
10195   myLastCreatedNodes.Clear();
10196
10197   MESSAGE ("::::SewSideElements()");
10198   if ( theSide1.size() != theSide2.size() )
10199     return SEW_DIFF_NB_OF_ELEMENTS;
10200
10201   Sew_Error aResult = SEW_OK;
10202   // Algo:
10203   // 1. Build set of faces representing each side
10204   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10205   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10206
10207   // =======================================================================
10208   // 1. Build set of faces representing each side:
10209   // =======================================================================
10210   // a. build set of nodes belonging to faces
10211   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10212   // c. create temporary faces representing side of volumes if correspondent
10213   //    face does not exist
10214
10215   SMESHDS_Mesh* aMesh = GetMeshDS();
10216   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10217   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10218   TIDSortedElemSet             faceSet1, faceSet2;
10219   set<const SMDS_MeshElement*> volSet1,  volSet2;
10220   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10221   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10222   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10223   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10224   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10225   int iSide, iFace, iNode;
10226
10227   list<const SMDS_MeshElement* > tempFaceList;
10228   for ( iSide = 0; iSide < 2; iSide++ ) {
10229     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10230     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10231     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10232     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10233     set<const SMDS_MeshElement*>::iterator vIt;
10234     TIDSortedElemSet::iterator eIt;
10235     set<const SMDS_MeshNode*>::iterator    nIt;
10236
10237     // check that given nodes belong to given elements
10238     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10239     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10240     int firstIndex = -1, secondIndex = -1;
10241     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10242       const SMDS_MeshElement* elem = *eIt;
10243       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10244       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10245       if ( firstIndex > -1 && secondIndex > -1 ) break;
10246     }
10247     if ( firstIndex < 0 || secondIndex < 0 ) {
10248       // we can simply return until temporary faces created
10249       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10250     }
10251
10252     // -----------------------------------------------------------
10253     // 1a. Collect nodes of existing faces
10254     //     and build set of face nodes in order to detect missing
10255     //     faces corresponding to sides of volumes
10256     // -----------------------------------------------------------
10257
10258     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10259
10260     // loop on the given element of a side
10261     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10262       //const SMDS_MeshElement* elem = *eIt;
10263       const SMDS_MeshElement* elem = *eIt;
10264       if ( elem->GetType() == SMDSAbs_Face ) {
10265         faceSet->insert( elem );
10266         set <const SMDS_MeshNode*> faceNodeSet;
10267         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10268         while ( nodeIt->more() ) {
10269           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10270           nodeSet->insert( n );
10271           faceNodeSet.insert( n );
10272         }
10273         setOfFaceNodeSet.insert( faceNodeSet );
10274       }
10275       else if ( elem->GetType() == SMDSAbs_Volume )
10276         volSet->insert( elem );
10277     }
10278     // ------------------------------------------------------------------------------
10279     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10280     // ------------------------------------------------------------------------------
10281
10282     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10283       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10284       while ( fIt->more() ) { // loop on faces sharing a node
10285         const SMDS_MeshElement* f = fIt->next();
10286         if ( faceSet->find( f ) == faceSet->end() ) {
10287           // check if all nodes are in nodeSet and
10288           // complete setOfFaceNodeSet if they are
10289           set <const SMDS_MeshNode*> faceNodeSet;
10290           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10291           bool allInSet = true;
10292           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10293             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10294             if ( nodeSet->find( n ) == nodeSet->end() )
10295               allInSet = false;
10296             else
10297               faceNodeSet.insert( n );
10298           }
10299           if ( allInSet ) {
10300             faceSet->insert( f );
10301             setOfFaceNodeSet.insert( faceNodeSet );
10302           }
10303         }
10304       }
10305     }
10306
10307     // -------------------------------------------------------------------------
10308     // 1c. Create temporary faces representing sides of volumes if correspondent
10309     //     face does not exist
10310     // -------------------------------------------------------------------------
10311
10312     if ( !volSet->empty() ) {
10313       //int nodeSetSize = nodeSet->size();
10314
10315       // loop on given volumes
10316       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10317         SMDS_VolumeTool vol (*vIt);
10318         // loop on volume faces: find free faces
10319         // --------------------------------------
10320         list<const SMDS_MeshElement* > freeFaceList;
10321         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10322           if ( !vol.IsFreeFace( iFace ))
10323             continue;
10324           // check if there is already a face with same nodes in a face set
10325           const SMDS_MeshElement* aFreeFace = 0;
10326           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10327           int nbNodes = vol.NbFaceNodes( iFace );
10328           set <const SMDS_MeshNode*> faceNodeSet;
10329           vol.GetFaceNodes( iFace, faceNodeSet );
10330           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10331           if ( isNewFace ) {
10332             // no such a face is given but it still can exist, check it
10333             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10334             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10335           }
10336           if ( !aFreeFace ) {
10337             // create a temporary face
10338             if ( nbNodes == 3 ) {
10339               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10340               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10341             }
10342             else if ( nbNodes == 4 ) {
10343               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10344               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10345             }
10346             else {
10347               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10348               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10349               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10350             }
10351             if ( aFreeFace )
10352               tempFaceList.push_back( aFreeFace );
10353           }
10354
10355           if ( aFreeFace )
10356             freeFaceList.push_back( aFreeFace );
10357
10358         } // loop on faces of a volume
10359
10360         // choose one of several free faces of a volume
10361         // --------------------------------------------
10362         if ( freeFaceList.size() > 1 ) {
10363           // choose a face having max nb of nodes shared by other elems of a side
10364           int maxNbNodes = -1;
10365           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10366           while ( fIt != freeFaceList.end() ) { // loop on free faces
10367             int nbSharedNodes = 0;
10368             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10369             while ( nodeIt->more() ) { // loop on free face nodes
10370               const SMDS_MeshNode* n =
10371                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10372               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10373               while ( invElemIt->more() ) {
10374                 const SMDS_MeshElement* e = invElemIt->next();
10375                 nbSharedNodes += faceSet->count( e );
10376                 nbSharedNodes += elemSet->count( e );
10377               }
10378             }
10379             if ( nbSharedNodes > maxNbNodes ) {
10380               maxNbNodes = nbSharedNodes;
10381               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10382             }
10383             else if ( nbSharedNodes == maxNbNodes ) {
10384               fIt++;
10385             }
10386             else {
10387               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10388             }
10389           }
10390           if ( freeFaceList.size() > 1 )
10391           {
10392             // could not choose one face, use another way
10393             // choose a face most close to the bary center of the opposite side
10394             gp_XYZ aBC( 0., 0., 0. );
10395             set <const SMDS_MeshNode*> addedNodes;
10396             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10397             eIt = elemSet2->begin();
10398             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10399               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10400               while ( nodeIt->more() ) { // loop on free face nodes
10401                 const SMDS_MeshNode* n =
10402                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10403                 if ( addedNodes.insert( n ).second )
10404                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10405               }
10406             }
10407             aBC /= addedNodes.size();
10408             double minDist = DBL_MAX;
10409             fIt = freeFaceList.begin();
10410             while ( fIt != freeFaceList.end() ) { // loop on free faces
10411               double dist = 0;
10412               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10413               while ( nodeIt->more() ) { // loop on free face nodes
10414                 const SMDS_MeshNode* n =
10415                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10416                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10417                 dist += ( aBC - p ).SquareModulus();
10418               }
10419               if ( dist < minDist ) {
10420                 minDist = dist;
10421                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10422               }
10423               else
10424                 fIt = freeFaceList.erase( fIt++ );
10425             }
10426           }
10427         } // choose one of several free faces of a volume
10428
10429         if ( freeFaceList.size() == 1 ) {
10430           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10431           faceSet->insert( aFreeFace );
10432           // complete a node set with nodes of a found free face
10433           //           for ( iNode = 0; iNode < ; iNode++ )
10434           //             nodeSet->insert( fNodes[ iNode ] );
10435         }
10436
10437       } // loop on volumes of a side
10438
10439       //       // complete a set of faces if new nodes in a nodeSet appeared
10440       //       // ----------------------------------------------------------
10441       //       if ( nodeSetSize != nodeSet->size() ) {
10442       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10443       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10444       //           while ( fIt->more() ) { // loop on faces sharing a node
10445       //             const SMDS_MeshElement* f = fIt->next();
10446       //             if ( faceSet->find( f ) == faceSet->end() ) {
10447       //               // check if all nodes are in nodeSet and
10448       //               // complete setOfFaceNodeSet if they are
10449       //               set <const SMDS_MeshNode*> faceNodeSet;
10450       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10451       //               bool allInSet = true;
10452       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10453       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10454       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10455       //                   allInSet = false;
10456       //                 else
10457       //                   faceNodeSet.insert( n );
10458       //               }
10459       //               if ( allInSet ) {
10460       //                 faceSet->insert( f );
10461       //                 setOfFaceNodeSet.insert( faceNodeSet );
10462       //               }
10463       //             }
10464       //           }
10465       //         }
10466       //       }
10467     } // Create temporary faces, if there are volumes given
10468   } // loop on sides
10469
10470   if ( faceSet1.size() != faceSet2.size() ) {
10471     // delete temporary faces: they are in reverseElements of actual nodes
10472 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10473 //    while ( tmpFaceIt->more() )
10474 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10475 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10476 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10477 //      aMesh->RemoveElement(*tmpFaceIt);
10478     MESSAGE("Diff nb of faces");
10479     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10480   }
10481
10482   // ============================================================
10483   // 2. Find nodes to merge:
10484   //              bind a node to remove to a node to put instead
10485   // ============================================================
10486
10487   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10488   if ( theFirstNode1 != theFirstNode2 )
10489     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10490   if ( theSecondNode1 != theSecondNode2 )
10491     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10492
10493   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10494   set< long > linkIdSet; // links to process
10495   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10496
10497   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10498   list< NLink > linkList[2];
10499   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10500   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10501   // loop on links in linkList; find faces by links and append links
10502   // of the found faces to linkList
10503   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10504   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10505   {
10506     NLink link[] = { *linkIt[0], *linkIt[1] };
10507     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10508     if ( !linkIdSet.count( linkID ) )
10509       continue;
10510
10511     // by links, find faces in the face sets,
10512     // and find indices of link nodes in the found faces;
10513     // in a face set, there is only one or no face sharing a link
10514     // ---------------------------------------------------------------
10515
10516     const SMDS_MeshElement* face[] = { 0, 0 };
10517     vector<const SMDS_MeshNode*> fnodes[2];
10518     int iLinkNode[2][2];
10519     TIDSortedElemSet avoidSet;
10520     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10521       const SMDS_MeshNode* n1 = link[iSide].first;
10522       const SMDS_MeshNode* n2 = link[iSide].second;
10523       //cout << "Side " << iSide << " ";
10524       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10525       // find a face by two link nodes
10526       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10527                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10528       if ( face[ iSide ])
10529       {
10530         //cout << " F " << face[ iSide]->GetID() <<endl;
10531         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10532         // put face nodes to fnodes
10533         if ( face[ iSide ]->IsQuadratic() )
10534         {
10535           // use interlaced nodes iterator
10536           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10537           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10538           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10539           while ( nIter->more() )
10540             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10541         }
10542         else
10543         {
10544           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10545                                   face[ iSide ]->end_nodes() );
10546         }
10547         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10548       }
10549     }
10550
10551     // check similarity of elements of the sides
10552     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10553       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10554       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10555         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10556       }
10557       else {
10558         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10559       }
10560       break; // do not return because it's necessary to remove tmp faces
10561     }
10562
10563     // set nodes to merge
10564     // -------------------
10565
10566     if ( face[0] && face[1] )  {
10567       const int nbNodes = face[0]->NbNodes();
10568       if ( nbNodes != face[1]->NbNodes() ) {
10569         MESSAGE("Diff nb of face nodes");
10570         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10571         break; // do not return because it s necessary to remove tmp faces
10572       }
10573       bool reverse[] = { false, false }; // order of nodes in the link
10574       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10575         // analyse link orientation in faces
10576         int i1 = iLinkNode[ iSide ][ 0 ];
10577         int i2 = iLinkNode[ iSide ][ 1 ];
10578         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10579       }
10580       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10581       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10582       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10583       {
10584         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10585                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10586       }
10587
10588       // add other links of the faces to linkList
10589       // -----------------------------------------
10590
10591       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10592         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10593         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10594         if ( !iter_isnew.second ) { // already in a set: no need to process
10595           linkIdSet.erase( iter_isnew.first );
10596         }
10597         else // new in set == encountered for the first time: add
10598         {
10599           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10600           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10601           linkList[0].push_back ( NLink( n1, n2 ));
10602           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10603         }
10604       }
10605     } // 2 faces found
10606
10607     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10608       break;
10609
10610   } // loop on link lists
10611
10612   if ( aResult == SEW_OK &&
10613        ( //linkIt[0] != linkList[0].end() ||
10614          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10615     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10616              " " << (faceSetPtr[1]->empty()));
10617     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10618   }
10619
10620   // ====================================================================
10621   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10622   // ====================================================================
10623
10624   // delete temporary faces
10625 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10626 //  while ( tmpFaceIt->more() )
10627 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10628   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10629   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10630     aMesh->RemoveElement(*tmpFaceIt);
10631
10632   if ( aResult != SEW_OK)
10633     return aResult;
10634
10635   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10636   // loop on nodes replacement map
10637   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10638   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10639     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10640       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10641       nodeIDsToRemove.push_back( nToRemove->GetID() );
10642       // loop on elements sharing nToRemove
10643       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10644       while ( invElemIt->more() ) {
10645         const SMDS_MeshElement* e = invElemIt->next();
10646         // get a new suite of nodes: make replacement
10647         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10648         vector< const SMDS_MeshNode*> nodes( nbNodes );
10649         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10650         while ( nIt->more() ) {
10651           const SMDS_MeshNode* n =
10652             static_cast<const SMDS_MeshNode*>( nIt->next() );
10653           nnIt = nReplaceMap.find( n );
10654           if ( nnIt != nReplaceMap.end() ) {
10655             nbReplaced++;
10656             n = (*nnIt).second;
10657           }
10658           nodes[ i++ ] = n;
10659         }
10660         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10661         //         elemIDsToRemove.push_back( e->GetID() );
10662         //       else
10663         if ( nbReplaced )
10664           {
10665             SMDSAbs_ElementType etyp = e->GetType();
10666             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10667             if (newElem)
10668               {
10669                 myLastCreatedElems.Append(newElem);
10670                 AddToSameGroups(newElem, e, aMesh);
10671                 int aShapeId = e->getshapeId();
10672                 if ( aShapeId )
10673                   {
10674                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10675                   }
10676               }
10677             aMesh->RemoveElement(e);
10678           }
10679       }
10680     }
10681
10682   Remove( nodeIDsToRemove, true );
10683
10684   return aResult;
10685 }
10686
10687 //================================================================================
10688 /*!
10689  * \brief Find corresponding nodes in two sets of faces
10690  * \param theSide1 - first face set
10691  * \param theSide2 - second first face
10692  * \param theFirstNode1 - a boundary node of set 1
10693  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10694  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10695  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10696  * \param nReplaceMap - output map of corresponding nodes
10697  * \return bool  - is a success or not
10698  */
10699 //================================================================================
10700
10701 #ifdef _DEBUG_
10702 //#define DEBUG_MATCHING_NODES
10703 #endif
10704
10705 SMESH_MeshEditor::Sew_Error
10706 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10707                                     set<const SMDS_MeshElement*>& theSide2,
10708                                     const SMDS_MeshNode*          theFirstNode1,
10709                                     const SMDS_MeshNode*          theFirstNode2,
10710                                     const SMDS_MeshNode*          theSecondNode1,
10711                                     const SMDS_MeshNode*          theSecondNode2,
10712                                     TNodeNodeMap &                nReplaceMap)
10713 {
10714   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10715
10716   nReplaceMap.clear();
10717   if ( theFirstNode1 != theFirstNode2 )
10718     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10719   if ( theSecondNode1 != theSecondNode2 )
10720     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10721
10722   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10723   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10724
10725   list< NLink > linkList[2];
10726   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10727   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10728
10729   // loop on links in linkList; find faces by links and append links
10730   // of the found faces to linkList
10731   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10732   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10733     NLink link[] = { *linkIt[0], *linkIt[1] };
10734     if ( linkSet.find( link[0] ) == linkSet.end() )
10735       continue;
10736
10737     // by links, find faces in the face sets,
10738     // and find indices of link nodes in the found faces;
10739     // in a face set, there is only one or no face sharing a link
10740     // ---------------------------------------------------------------
10741
10742     const SMDS_MeshElement* face[] = { 0, 0 };
10743     list<const SMDS_MeshNode*> notLinkNodes[2];
10744     //bool reverse[] = { false, false }; // order of notLinkNodes
10745     int nbNodes[2];
10746     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10747     {
10748       const SMDS_MeshNode* n1 = link[iSide].first;
10749       const SMDS_MeshNode* n2 = link[iSide].second;
10750       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10751       set< const SMDS_MeshElement* > facesOfNode1;
10752       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10753       {
10754         // during a loop of the first node, we find all faces around n1,
10755         // during a loop of the second node, we find one face sharing both n1 and n2
10756         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10757         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10758         while ( fIt->more() ) { // loop on faces sharing a node
10759           const SMDS_MeshElement* f = fIt->next();
10760           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10761               ! facesOfNode1.insert( f ).second ) // f encounters twice
10762           {
10763             if ( face[ iSide ] ) {
10764               MESSAGE( "2 faces per link " );
10765               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10766             }
10767             face[ iSide ] = f;
10768             faceSet->erase( f );
10769
10770             // get not link nodes
10771             int nbN = f->NbNodes();
10772             if ( f->IsQuadratic() )
10773               nbN /= 2;
10774             nbNodes[ iSide ] = nbN;
10775             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10776             int i1 = f->GetNodeIndex( n1 );
10777             int i2 = f->GetNodeIndex( n2 );
10778             int iEnd = nbN, iBeg = -1, iDelta = 1;
10779             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10780             if ( reverse ) {
10781               std::swap( iEnd, iBeg ); iDelta = -1;
10782             }
10783             int i = i2;
10784             while ( true ) {
10785               i += iDelta;
10786               if ( i == iEnd ) i = iBeg + iDelta;
10787               if ( i == i1 ) break;
10788               nodes.push_back ( f->GetNode( i ) );
10789             }
10790           }
10791         }
10792       }
10793     }
10794     // check similarity of elements of the sides
10795     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10796       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10797       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10798         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10799       }
10800       else {
10801         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10802       }
10803     }
10804
10805     // set nodes to merge
10806     // -------------------
10807
10808     if ( face[0] && face[1] )  {
10809       if ( nbNodes[0] != nbNodes[1] ) {
10810         MESSAGE("Diff nb of face nodes");
10811         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10812       }
10813 #ifdef DEBUG_MATCHING_NODES
10814       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10815                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10816                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10817 #endif
10818       int nbN = nbNodes[0];
10819       {
10820         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10821         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10822         for ( int i = 0 ; i < nbN - 2; ++i ) {
10823 #ifdef DEBUG_MATCHING_NODES
10824           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10825 #endif
10826           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10827         }
10828       }
10829
10830       // add other links of the face 1 to linkList
10831       // -----------------------------------------
10832
10833       const SMDS_MeshElement* f0 = face[0];
10834       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10835       for ( int i = 0; i < nbN; i++ )
10836       {
10837         const SMDS_MeshNode* n2 = f0->GetNode( i );
10838         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10839           linkSet.insert( SMESH_TLink( n1, n2 ));
10840         if ( !iter_isnew.second ) { // already in a set: no need to process
10841           linkSet.erase( iter_isnew.first );
10842         }
10843         else // new in set == encountered for the first time: add
10844         {
10845 #ifdef DEBUG_MATCHING_NODES
10846           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10847                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10848 #endif
10849           linkList[0].push_back ( NLink( n1, n2 ));
10850           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10851         }
10852         n1 = n2;
10853       }
10854     } // 2 faces found
10855   } // loop on link lists
10856
10857   return SEW_OK;
10858 }
10859
10860 //================================================================================
10861 /*!
10862   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10863   \param theElems - the list of elements (edges or faces) to be replicated
10864   The nodes for duplication could be found from these elements
10865   \param theNodesNot - list of nodes to NOT replicate
10866   \param theAffectedElems - the list of elements (cells and edges) to which the
10867   replicated nodes should be associated to.
10868   \return TRUE if operation has been completed successfully, FALSE otherwise
10869 */
10870 //================================================================================
10871
10872 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10873                                     const TIDSortedElemSet& theNodesNot,
10874                                     const TIDSortedElemSet& theAffectedElems )
10875 {
10876   myLastCreatedElems.Clear();
10877   myLastCreatedNodes.Clear();
10878
10879   if ( theElems.size() == 0 )
10880     return false;
10881
10882   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10883   if ( !aMeshDS )
10884     return false;
10885
10886   bool res = false;
10887   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10888   // duplicate elements and nodes
10889   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10890   // replce nodes by duplications
10891   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10892   return res;
10893 }
10894
10895 //================================================================================
10896 /*!
10897   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10898   \param theMeshDS - mesh instance
10899   \param theElems - the elements replicated or modified (nodes should be changed)
10900   \param theNodesNot - nodes to NOT replicate
10901   \param theNodeNodeMap - relation of old node to new created node
10902   \param theIsDoubleElem - flag os to replicate element or modify
10903   \return TRUE if operation has been completed successfully, FALSE otherwise
10904 */
10905 //================================================================================
10906
10907 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10908                                     const TIDSortedElemSet& theElems,
10909                                     const TIDSortedElemSet& theNodesNot,
10910                                     std::map< const SMDS_MeshNode*,
10911                                     const SMDS_MeshNode* >& theNodeNodeMap,
10912                                     const bool theIsDoubleElem )
10913 {
10914   MESSAGE("doubleNodes");
10915   // iterate on through element and duplicate them (by nodes duplication)
10916   bool res = false;
10917   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10918   for ( ;  elemItr != theElems.end(); ++elemItr )
10919   {
10920     const SMDS_MeshElement* anElem = *elemItr;
10921     if (!anElem)
10922       continue;
10923
10924     bool isDuplicate = false;
10925     // duplicate nodes to duplicate element
10926     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10927     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10928     int ind = 0;
10929     while ( anIter->more() )
10930     {
10931
10932       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10933       SMDS_MeshNode* aNewNode = aCurrNode;
10934       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10935         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10936       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10937       {
10938         // duplicate node
10939         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10940         theNodeNodeMap[ aCurrNode ] = aNewNode;
10941         myLastCreatedNodes.Append( aNewNode );
10942       }
10943       isDuplicate |= (aCurrNode != aNewNode);
10944       newNodes[ ind++ ] = aNewNode;
10945     }
10946     if ( !isDuplicate )
10947       continue;
10948
10949     if ( theIsDoubleElem )
10950       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10951     else
10952       {
10953       MESSAGE("ChangeElementNodes");
10954       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10955       }
10956     res = true;
10957   }
10958   return res;
10959 }
10960
10961 //================================================================================
10962 /*!
10963   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10964   \param theNodes - identifiers of nodes to be doubled
10965   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10966          nodes. If list of element identifiers is empty then nodes are doubled but
10967          they not assigned to elements
10968   \return TRUE if operation has been completed successfully, FALSE otherwise
10969 */
10970 //================================================================================
10971
10972 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10973                                     const std::list< int >& theListOfModifiedElems )
10974 {
10975   MESSAGE("DoubleNodes");
10976   myLastCreatedElems.Clear();
10977   myLastCreatedNodes.Clear();
10978
10979   if ( theListOfNodes.size() == 0 )
10980     return false;
10981
10982   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10983   if ( !aMeshDS )
10984     return false;
10985
10986   // iterate through nodes and duplicate them
10987
10988   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10989
10990   std::list< int >::const_iterator aNodeIter;
10991   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10992   {
10993     int aCurr = *aNodeIter;
10994     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10995     if ( !aNode )
10996       continue;
10997
10998     // duplicate node
10999
11000     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11001     if ( aNewNode )
11002     {
11003       anOldNodeToNewNode[ aNode ] = aNewNode;
11004       myLastCreatedNodes.Append( aNewNode );
11005     }
11006   }
11007
11008   // Create map of new nodes for modified elements
11009
11010   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
11011
11012   std::list< int >::const_iterator anElemIter;
11013   for ( anElemIter = theListOfModifiedElems.begin();
11014         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
11015   {
11016     int aCurr = *anElemIter;
11017     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
11018     if ( !anElem )
11019       continue;
11020
11021     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
11022
11023     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11024     int ind = 0;
11025     while ( anIter->more() )
11026     {
11027       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
11028       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
11029       {
11030         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
11031         aNodeArr[ ind++ ] = aNewNode;
11032       }
11033       else
11034         aNodeArr[ ind++ ] = aCurrNode;
11035     }
11036     anElemToNodes[ anElem ] = aNodeArr;
11037   }
11038
11039   // Change nodes of elements
11040
11041   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
11042     anElemToNodesIter = anElemToNodes.begin();
11043   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
11044   {
11045     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
11046     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
11047     if ( anElem )
11048       {
11049       MESSAGE("ChangeElementNodes");
11050       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
11051       }
11052   }
11053
11054   return true;
11055 }
11056
11057 namespace {
11058
11059   //================================================================================
11060   /*!
11061   \brief Check if element located inside shape
11062   \return TRUE if IN or ON shape, FALSE otherwise
11063   */
11064   //================================================================================
11065
11066   template<class Classifier>
11067   bool isInside(const SMDS_MeshElement* theElem,
11068                 Classifier&             theClassifier,
11069                 const double            theTol)
11070   {
11071     gp_XYZ centerXYZ (0, 0, 0);
11072     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11073     while (aNodeItr->more())
11074       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
11075
11076     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11077     theClassifier.Perform(aPnt, theTol);
11078     TopAbs_State aState = theClassifier.State();
11079     return (aState == TopAbs_IN || aState == TopAbs_ON );
11080   }
11081
11082   //================================================================================
11083   /*!
11084    * \brief Classifier of the 3D point on the TopoDS_Face
11085    *        with interaface suitable for isInside()
11086    */
11087   //================================================================================
11088
11089   struct _FaceClassifier
11090   {
11091     Extrema_ExtPS       _extremum;
11092     BRepAdaptor_Surface _surface;
11093     TopAbs_State        _state;
11094
11095     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11096     {
11097       _extremum.Initialize( _surface,
11098                             _surface.FirstUParameter(), _surface.LastUParameter(),
11099                             _surface.FirstVParameter(), _surface.LastVParameter(),
11100                             _surface.Tolerance(), _surface.Tolerance() );
11101     }
11102     void Perform(const gp_Pnt& aPnt, double theTol)
11103     {
11104       _state = TopAbs_OUT;
11105       _extremum.Perform(aPnt);
11106       if ( _extremum.IsDone() )
11107         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11108 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11109           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11110 #else
11111           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11112 #endif
11113     }
11114     TopAbs_State State() const
11115     {
11116       return _state;
11117     }
11118   };
11119 }
11120
11121 //================================================================================
11122 /*!
11123   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11124   This method is the first step of DoubleNodeElemGroupsInRegion.
11125   \param theElems - list of groups of elements (edges or faces) to be replicated
11126   \param theNodesNot - list of groups of nodes not to replicated
11127   \param theShape - shape to detect affected elements (element which geometric center
11128          located on or inside shape).
11129          The replicated nodes should be associated to affected elements.
11130   \return groups of affected elements
11131   \sa DoubleNodeElemGroupsInRegion()
11132  */
11133 //================================================================================
11134
11135 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11136                                                    const TIDSortedElemSet& theNodesNot,
11137                                                    const TopoDS_Shape&     theShape,
11138                                                    TIDSortedElemSet&       theAffectedElems)
11139 {
11140   if ( theShape.IsNull() )
11141     return false;
11142
11143   const double aTol = Precision::Confusion();
11144   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11145   auto_ptr<_FaceClassifier>              aFaceClassifier;
11146   if ( theShape.ShapeType() == TopAbs_SOLID )
11147   {
11148     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11149     bsc3d->PerformInfinitePoint(aTol);
11150   }
11151   else if (theShape.ShapeType() == TopAbs_FACE )
11152   {
11153     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11154   }
11155
11156   // iterates on indicated elements and get elements by back references from their nodes
11157   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11158   for ( ;  elemItr != theElems.end(); ++elemItr )
11159   {
11160     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11161     if (!anElem)
11162       continue;
11163
11164     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11165     while ( nodeItr->more() )
11166     {
11167       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11168       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11169         continue;
11170       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11171       while ( backElemItr->more() )
11172       {
11173         const SMDS_MeshElement* curElem = backElemItr->next();
11174         if ( curElem && theElems.find(curElem) == theElems.end() &&
11175              ( bsc3d.get() ?
11176                isInside( curElem, *bsc3d, aTol ) :
11177                isInside( curElem, *aFaceClassifier, aTol )))
11178           theAffectedElems.insert( curElem );
11179       }
11180     }
11181   }
11182   return true;
11183 }
11184
11185 //================================================================================
11186 /*!
11187   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11188   \param theElems - group of of elements (edges or faces) to be replicated
11189   \param theNodesNot - group of nodes not to replicate
11190   \param theShape - shape to detect affected elements (element which geometric center
11191   located on or inside shape).
11192   The replicated nodes should be associated to affected elements.
11193   \return TRUE if operation has been completed successfully, FALSE otherwise
11194 */
11195 //================================================================================
11196
11197 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11198                                             const TIDSortedElemSet& theNodesNot,
11199                                             const TopoDS_Shape&     theShape )
11200 {
11201   if ( theShape.IsNull() )
11202     return false;
11203
11204   const double aTol = Precision::Confusion();
11205   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11206   auto_ptr<_FaceClassifier>              aFaceClassifier;
11207   if ( theShape.ShapeType() == TopAbs_SOLID )
11208   {
11209     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11210     bsc3d->PerformInfinitePoint(aTol);
11211   }
11212   else if (theShape.ShapeType() == TopAbs_FACE )
11213   {
11214     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11215   }
11216
11217   // iterates on indicated elements and get elements by back references from their nodes
11218   TIDSortedElemSet anAffected;
11219   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11220   for ( ;  elemItr != theElems.end(); ++elemItr )
11221   {
11222     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11223     if (!anElem)
11224       continue;
11225
11226     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11227     while ( nodeItr->more() )
11228     {
11229       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11230       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11231         continue;
11232       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11233       while ( backElemItr->more() )
11234       {
11235         const SMDS_MeshElement* curElem = backElemItr->next();
11236         if ( curElem && theElems.find(curElem) == theElems.end() &&
11237              ( bsc3d.get() ?
11238                isInside( curElem, *bsc3d, aTol ) :
11239                isInside( curElem, *aFaceClassifier, aTol )))
11240           anAffected.insert( curElem );
11241       }
11242     }
11243   }
11244   return DoubleNodes( theElems, theNodesNot, anAffected );
11245 }
11246
11247 /*!
11248  *  \brief compute an oriented angle between two planes defined by four points.
11249  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11250  *  @param p0 base of the rotation axe
11251  *  @param p1 extremity of the rotation axe
11252  *  @param g1 belongs to the first plane
11253  *  @param g2 belongs to the second plane
11254  */
11255 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11256 {
11257 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11258 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11259 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11260 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11261   gp_Vec vref(p0, p1);
11262   gp_Vec v1(p0, g1);
11263   gp_Vec v2(p0, g2);
11264   gp_Vec n1 = vref.Crossed(v1);
11265   gp_Vec n2 = vref.Crossed(v2);
11266   return n2.AngleWithRef(n1, vref);
11267 }
11268
11269 /*!
11270  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11271  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11272  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11273  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11274  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11275  * 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.
11276  * 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.
11277  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11278  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11279  * @param theElems - list of groups of volumes, where a group of volume is a set of
11280  * SMDS_MeshElements sorted by Id.
11281  * @param createJointElems - if TRUE, create the elements
11282  * @return TRUE if operation has been completed successfully, FALSE otherwise
11283  */
11284 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11285                                                      bool createJointElems)
11286 {
11287   MESSAGE("----------------------------------------------");
11288   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11289   MESSAGE("----------------------------------------------");
11290
11291   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11292   meshDS->BuildDownWardConnectivity(true);
11293   CHRONO(50);
11294   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11295
11296   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11297   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11298   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11299
11300   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11301   std::map<int,int>celldom; // cell vtkId --> domain
11302   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11303   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11304   faceDomains.clear();
11305   celldom.clear();
11306   cellDomains.clear();
11307   nodeDomains.clear();
11308   std::map<int,int> emptyMap;
11309   std::set<int> emptySet;
11310   emptyMap.clear();
11311
11312   MESSAGE(".. Number of domains :"<<theElems.size());
11313
11314   // Check if the domains do not share an element
11315   for (int idom = 0; idom < theElems.size()-1; idom++)
11316     {
11317 //       MESSAGE("... Check of domain #" << idom);
11318       const TIDSortedElemSet& domain = theElems[idom];
11319       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11320       for (; elemItr != domain.end(); ++elemItr)
11321         {
11322           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11323           int idombisdeb = idom + 1 ;
11324           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11325           {
11326             const TIDSortedElemSet& domainbis = theElems[idombis];
11327             if ( domainbis.count(anElem) )
11328             {
11329               MESSAGE(".... Domain #" << idom);
11330               MESSAGE(".... Domain #" << idombis);
11331               throw SALOME_Exception("The domains are not disjoint.");
11332               return false ;
11333             }
11334           }
11335         }
11336     }
11337
11338   for (int idom = 0; idom < theElems.size(); idom++)
11339     {
11340
11341       // --- build a map (face to duplicate --> volume to modify)
11342       //     with all the faces shared by 2 domains (group of elements)
11343       //     and corresponding volume of this domain, for each shared face.
11344       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11345
11346       MESSAGE("... Neighbors of domain #" << idom);
11347       const TIDSortedElemSet& domain = theElems[idom];
11348       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11349       for (; elemItr != domain.end(); ++elemItr)
11350         {
11351           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11352           if (!anElem)
11353             continue;
11354           int vtkId = anElem->getVtkId();
11355           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11356           int neighborsVtkIds[NBMAXNEIGHBORS];
11357           int downIds[NBMAXNEIGHBORS];
11358           unsigned char downTypes[NBMAXNEIGHBORS];
11359           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11360           for (int n = 0; n < nbNeighbors; n++)
11361             {
11362               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11363               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11364               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11365                 {
11366                   bool ok = false ;
11367                   for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
11368                   {
11369                     // MESSAGE("Domain " << idombis);
11370                     const TIDSortedElemSet& domainbis = theElems[idombis];
11371                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11372                   }
11373                   if ( ok ) // the characteristics of the face is stored
11374                   {
11375                     DownIdType face(downIds[n], downTypes[n]);
11376                     if (!faceDomains.count(face))
11377                       faceDomains[face] = emptyMap; // create an empty entry for face
11378                     if (!faceDomains[face].count(idom))
11379                       {
11380                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11381                         celldom[vtkId] = idom;
11382                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11383                       }
11384                   }
11385                 }
11386             }
11387         }
11388     }
11389
11390   //MESSAGE("Number of shared faces " << faceDomains.size());
11391   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11392
11393   // --- explore the shared faces domain by domain,
11394   //     explore the nodes of the face and see if they belong to a cell in the domain,
11395   //     which has only a node or an edge on the border (not a shared face)
11396
11397   for (int idomain = 0; idomain < theElems.size(); idomain++)
11398     {
11399       //MESSAGE("Domain " << idomain);
11400       const TIDSortedElemSet& domain = theElems[idomain];
11401       itface = faceDomains.begin();
11402       for (; itface != faceDomains.end(); ++itface)
11403         {
11404           std::map<int, int> domvol = itface->second;
11405           if (!domvol.count(idomain))
11406             continue;
11407           DownIdType face = itface->first;
11408           //MESSAGE(" --- face " << face.cellId);
11409           std::set<int> oldNodes;
11410           oldNodes.clear();
11411           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11412           std::set<int>::iterator itn = oldNodes.begin();
11413           for (; itn != oldNodes.end(); ++itn)
11414             {
11415               int oldId = *itn;
11416               //MESSAGE("     node " << oldId);
11417               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11418               for (int i=0; i<l.ncells; i++)
11419                 {
11420                   int vtkId = l.cells[i];
11421                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11422                   if (!domain.count(anElem))
11423                     continue;
11424                   int vtkType = grid->GetCellType(vtkId);
11425                   int downId = grid->CellIdToDownId(vtkId);
11426                   if (downId < 0)
11427                     {
11428                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11429                       continue; // not OK at this stage of the algorithm:
11430                                 //no cells created after BuildDownWardConnectivity
11431                     }
11432                   DownIdType aCell(downId, vtkType);
11433                   if (!cellDomains.count(aCell))
11434                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11435                   cellDomains[aCell][idomain] = vtkId;
11436                   celldom[vtkId] = idomain;
11437                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11438                 }
11439             }
11440         }
11441     }
11442
11443   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11444   //     for each shared face, get the nodes
11445   //     for each node, for each domain of the face, create a clone of the node
11446
11447   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11448   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11449   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11450
11451   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11452   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11453   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11454
11455   MESSAGE(".. Duplication of the nodes");
11456   for (int idomain = 0; idomain < theElems.size(); idomain++)
11457     {
11458       itface = faceDomains.begin();
11459       for (; itface != faceDomains.end(); ++itface)
11460         {
11461           std::map<int, int> domvol = itface->second;
11462           if (!domvol.count(idomain))
11463             continue;
11464           DownIdType face = itface->first;
11465           //MESSAGE(" --- face " << face.cellId);
11466           std::set<int> oldNodes;
11467           oldNodes.clear();
11468           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11469           std::set<int>::iterator itn = oldNodes.begin();
11470           for (; itn != oldNodes.end(); ++itn)
11471             {
11472               int oldId = *itn;
11473               //MESSAGE("-+-+-a node " << oldId);
11474               if (!nodeDomains.count(oldId))
11475                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11476               if (nodeDomains[oldId].empty())
11477                 {
11478                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11479                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11480                 }
11481               std::map<int, int>::iterator itdom = domvol.begin();
11482               for (; itdom != domvol.end(); ++itdom)
11483                 {
11484                   int idom = itdom->first;
11485                   //MESSAGE("         domain " << idom);
11486                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11487                     {
11488                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11489                         {
11490                           vector<int> orderedDoms;
11491                           //MESSAGE("multiple node " << oldId);
11492                           if (mutipleNodes.count(oldId))
11493                             orderedDoms = mutipleNodes[oldId];
11494                           else
11495                             {
11496                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11497                               for (; it != nodeDomains[oldId].end(); ++it)
11498                                 orderedDoms.push_back(it->first);
11499                             }
11500                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11501                           //stringstream txt;
11502                           //for (int i=0; i<orderedDoms.size(); i++)
11503                           //  txt << orderedDoms[i] << " ";
11504                           //MESSAGE("orderedDoms " << txt.str());
11505                           mutipleNodes[oldId] = orderedDoms;
11506                         }
11507                       double *coords = grid->GetPoint(oldId);
11508                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11509                       int newId = newNode->getVtkId();
11510                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11511                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11512                     }
11513                 }
11514             }
11515         }
11516     }
11517
11518   MESSAGE(".. Creation of elements");
11519   for (int idomain = 0; idomain < theElems.size(); idomain++)
11520     {
11521       itface = faceDomains.begin();
11522       for (; itface != faceDomains.end(); ++itface)
11523         {
11524           std::map<int, int> domvol = itface->second;
11525           if (!domvol.count(idomain))
11526             continue;
11527           DownIdType face = itface->first;
11528           //MESSAGE(" --- face " << face.cellId);
11529           std::set<int> oldNodes;
11530           oldNodes.clear();
11531           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11532           int nbMultipleNodes = 0;
11533           std::set<int>::iterator itn = oldNodes.begin();
11534           for (; itn != oldNodes.end(); ++itn)
11535             {
11536               int oldId = *itn;
11537               if (mutipleNodes.count(oldId))
11538                 nbMultipleNodes++;
11539             }
11540           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11541             {
11542               //MESSAGE("multiple Nodes detected on a shared face");
11543               int downId = itface->first.cellId;
11544               unsigned char cellType = itface->first.cellType;
11545               // --- shared edge or shared face ?
11546               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11547                 {
11548                   int nodes[3];
11549                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11550                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11551                     if (mutipleNodes.count(nodes[i]))
11552                       if (!mutipleNodesToFace.count(nodes[i]))
11553                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11554                 }
11555               else // shared face (between two volumes)
11556                 {
11557                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11558                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11559                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11560                   for (int ie =0; ie < nbEdges; ie++)
11561                     {
11562                       int nodes[3];
11563                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11564                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11565                         {
11566                           vector<int> vn0 = mutipleNodes[nodes[0]];
11567                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11568                           vector<int> doms;
11569                           for (int i0 = 0; i0 < vn0.size(); i0++)
11570                             for (int i1 = 0; i1 < vn1.size(); i1++)
11571                               if (vn0[i0] == vn1[i1])
11572                                 doms.push_back(vn0[i0]);
11573                           if (doms.size() >2)
11574                             {
11575                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11576                               double *coords = grid->GetPoint(nodes[0]);
11577                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11578                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11579                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11580                               gp_Pnt gref;
11581                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11582                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11583                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11584                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11585                               for (int id=0; id < doms.size(); id++)
11586                                 {
11587                                   int idom = doms[id];
11588                                   for (int ivol=0; ivol<nbvol; ivol++)
11589                                     {
11590                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11591                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11592                                       if (theElems[idom].count(elem))
11593                                         {
11594                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11595                                           domvol[idom] = svol;
11596                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11597                                           double values[3];
11598                                           vtkIdType npts = 0;
11599                                           vtkIdType* pts = 0;
11600                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11601                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11602                                           if (id ==0)
11603                                             {
11604                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11605                                               angleDom[idom] = 0;
11606                                             }
11607                                           else
11608                                             {
11609                                               gp_Pnt g(values[0], values[1], values[2]);
11610                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11611                                               //MESSAGE("  angle=" << angleDom[idom]);
11612                                             }
11613                                           break;
11614                                         }
11615                                     }
11616                                 }
11617                               map<double, int> sortedDom; // sort domains by angle
11618                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11619                                 sortedDom[ia->second] = ia->first;
11620                               vector<int> vnodes;
11621                               vector<int> vdom;
11622                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11623                                 {
11624                                   vdom.push_back(ib->second);
11625                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11626                                 }
11627                               for (int ino = 0; ino < nbNodes; ino++)
11628                                 vnodes.push_back(nodes[ino]);
11629                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11630                             }
11631                         }
11632                     }
11633                 }
11634             }
11635         }
11636     }
11637
11638   // --- iterate on shared faces (volumes to modify, face to extrude)
11639   //     get node id's of the face (id SMDS = id VTK)
11640   //     create flat element with old and new nodes if requested
11641
11642   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11643   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11644
11645   std::map<int, std::map<long,int> > nodeQuadDomains;
11646   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11647
11648   MESSAGE(".. Creation of elements: simple junction");
11649   if (createJointElems)
11650     {
11651       int idg;
11652       string joints2DName = "joints2D";
11653       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11654       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11655       string joints3DName = "joints3D";
11656       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11657       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11658
11659       itface = faceDomains.begin();
11660       for (; itface != faceDomains.end(); ++itface)
11661         {
11662           DownIdType face = itface->first;
11663           std::set<int> oldNodes;
11664           std::set<int>::iterator itn;
11665           oldNodes.clear();
11666           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11667
11668           std::map<int, int> domvol = itface->second;
11669           std::map<int, int>::iterator itdom = domvol.begin();
11670           int dom1 = itdom->first;
11671           int vtkVolId = itdom->second;
11672           itdom++;
11673           int dom2 = itdom->first;
11674           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11675                                                              nodeQuadDomains);
11676           stringstream grpname;
11677           grpname << "j_";
11678           if (dom1 < dom2)
11679             grpname << dom1 << "_" << dom2;
11680           else
11681             grpname << dom2 << "_" << dom1;
11682           string namegrp = grpname.str();
11683           if (!mapOfJunctionGroups.count(namegrp))
11684             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11685           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11686           if (sgrp)
11687             sgrp->Add(vol->GetID());
11688           if (vol->GetType() == SMDSAbs_Volume)
11689             joints3DGrp->Add(vol->GetID());
11690           else if (vol->GetType() == SMDSAbs_Face)
11691             joints2DGrp->Add(vol->GetID());
11692         }
11693     }
11694
11695   // --- create volumes on multiple domain intersection if requested
11696   //     iterate on mutipleNodesToFace
11697   //     iterate on edgesMultiDomains
11698
11699   MESSAGE(".. Creation of elements: multiple junction");
11700   if (createJointElems)
11701     {
11702       // --- iterate on mutipleNodesToFace
11703
11704       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11705       for (; itn != mutipleNodesToFace.end(); ++itn)
11706         {
11707           int node = itn->first;
11708           vector<int> orderDom = itn->second;
11709           vector<vtkIdType> orderedNodes;
11710           for (int idom = 0; idom <orderDom.size(); idom++)
11711             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11712             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11713
11714             stringstream grpname;
11715             grpname << "m2j_";
11716             grpname << 0 << "_" << 0;
11717             int idg;
11718             string namegrp = grpname.str();
11719             if (!mapOfJunctionGroups.count(namegrp))
11720               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11721             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11722             if (sgrp)
11723               sgrp->Add(face->GetID());
11724         }
11725
11726       // --- iterate on edgesMultiDomains
11727
11728       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11729       for (; ite != edgesMultiDomains.end(); ++ite)
11730         {
11731           vector<int> nodes = ite->first;
11732           vector<int> orderDom = ite->second;
11733           vector<vtkIdType> orderedNodes;
11734           if (nodes.size() == 2)
11735             {
11736               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11737               for (int ino=0; ino < nodes.size(); ino++)
11738                 if (orderDom.size() == 3)
11739                   for (int idom = 0; idom <orderDom.size(); idom++)
11740                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11741                 else
11742                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11743                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11744               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11745
11746               int idg;
11747               string namegrp = "jointsMultiples";
11748               if (!mapOfJunctionGroups.count(namegrp))
11749                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11750               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11751               if (sgrp)
11752                 sgrp->Add(vol->GetID());
11753             }
11754           else
11755             {
11756               INFOS("Quadratic multiple joints not implemented");
11757               // TODO quadratic nodes
11758             }
11759         }
11760     }
11761
11762   // --- list the explicit faces and edges of the mesh that need to be modified,
11763   //     i.e. faces and edges built with one or more duplicated nodes.
11764   //     associate these faces or edges to their corresponding domain.
11765   //     only the first domain found is kept when a face or edge is shared
11766
11767   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11768   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11769   faceOrEdgeDom.clear();
11770   feDom.clear();
11771
11772   MESSAGE(".. Modification of elements");
11773   for (int idomain = 0; idomain < theElems.size(); idomain++)
11774     {
11775       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11776       for (; itnod != nodeDomains.end(); ++itnod)
11777         {
11778           int oldId = itnod->first;
11779           //MESSAGE("     node " << oldId);
11780           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11781           for (int i = 0; i < l.ncells; i++)
11782             {
11783               int vtkId = l.cells[i];
11784               int vtkType = grid->GetCellType(vtkId);
11785               int downId = grid->CellIdToDownId(vtkId);
11786               if (downId < 0)
11787                 continue; // new cells: not to be modified
11788               DownIdType aCell(downId, vtkType);
11789               int volParents[1000];
11790               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11791               for (int j = 0; j < nbvol; j++)
11792                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11793                   if (!feDom.count(vtkId))
11794                     {
11795                       feDom[vtkId] = idomain;
11796                       faceOrEdgeDom[aCell] = emptyMap;
11797                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11798                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11799                       //        << " type " << vtkType << " downId " << downId);
11800                     }
11801             }
11802         }
11803     }
11804
11805   // --- iterate on shared faces (volumes to modify, face to extrude)
11806   //     get node id's of the face
11807   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11808
11809   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11810   for (int m=0; m<3; m++)
11811     {
11812       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11813       itface = (*amap).begin();
11814       for (; itface != (*amap).end(); ++itface)
11815         {
11816           DownIdType face = itface->first;
11817           std::set<int> oldNodes;
11818           std::set<int>::iterator itn;
11819           oldNodes.clear();
11820           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11821           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11822           std::map<int, int> localClonedNodeIds;
11823
11824           std::map<int, int> domvol = itface->second;
11825           std::map<int, int>::iterator itdom = domvol.begin();
11826           for (; itdom != domvol.end(); ++itdom)
11827             {
11828               int idom = itdom->first;
11829               int vtkVolId = itdom->second;
11830               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11831               localClonedNodeIds.clear();
11832               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11833                 {
11834                   int oldId = *itn;
11835                   if (nodeDomains[oldId].count(idom))
11836                     {
11837                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11838                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11839                     }
11840                 }
11841               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11842             }
11843         }
11844     }
11845
11846   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11847   grid->BuildLinks();
11848
11849   CHRONOSTOP(50);
11850   counters::stats();
11851   return true;
11852 }
11853
11854 /*!
11855  * \brief Double nodes on some external faces and create flat elements.
11856  * Flat elements are mainly used by some types of mechanic calculations.
11857  *
11858  * Each group of the list must be constituted of faces.
11859  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11860  * @param theElems - list of groups of faces, where a group of faces is a set of
11861  * SMDS_MeshElements sorted by Id.
11862  * @return TRUE if operation has been completed successfully, FALSE otherwise
11863  */
11864 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11865 {
11866   MESSAGE("-------------------------------------------------");
11867   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11868   MESSAGE("-------------------------------------------------");
11869
11870   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11871
11872   // --- For each group of faces
11873   //     duplicate the nodes, create a flat element based on the face
11874   //     replace the nodes of the faces by their clones
11875
11876   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11877   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11878   clonedNodes.clear();
11879   intermediateNodes.clear();
11880   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11881   mapOfJunctionGroups.clear();
11882
11883   for (int idom = 0; idom < theElems.size(); idom++)
11884     {
11885       const TIDSortedElemSet& domain = theElems[idom];
11886       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11887       for (; elemItr != domain.end(); ++elemItr)
11888         {
11889           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11890           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11891           if (!aFace)
11892             continue;
11893           // MESSAGE("aFace=" << aFace->GetID());
11894           bool isQuad = aFace->IsQuadratic();
11895           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11896
11897           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11898
11899           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11900           while (nodeIt->more())
11901             {
11902               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11903               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11904               if (isMedium)
11905                 ln2.push_back(node);
11906               else
11907                 ln0.push_back(node);
11908
11909               const SMDS_MeshNode* clone = 0;
11910               if (!clonedNodes.count(node))
11911                 {
11912                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11913                   clonedNodes[node] = clone;
11914                 }
11915               else
11916                 clone = clonedNodes[node];
11917
11918               if (isMedium)
11919                 ln3.push_back(clone);
11920               else
11921                 ln1.push_back(clone);
11922
11923               const SMDS_MeshNode* inter = 0;
11924               if (isQuad && (!isMedium))
11925                 {
11926                   if (!intermediateNodes.count(node))
11927                     {
11928                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11929                       intermediateNodes[node] = inter;
11930                     }
11931                   else
11932                     inter = intermediateNodes[node];
11933                   ln4.push_back(inter);
11934                 }
11935             }
11936
11937           // --- extrude the face
11938
11939           vector<const SMDS_MeshNode*> ln;
11940           SMDS_MeshVolume* vol = 0;
11941           vtkIdType aType = aFace->GetVtkType();
11942           switch (aType)
11943           {
11944             case VTK_TRIANGLE:
11945               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11946               // MESSAGE("vol prism " << vol->GetID());
11947               ln.push_back(ln1[0]);
11948               ln.push_back(ln1[1]);
11949               ln.push_back(ln1[2]);
11950               break;
11951             case VTK_QUAD:
11952               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11953               // MESSAGE("vol hexa " << vol->GetID());
11954               ln.push_back(ln1[0]);
11955               ln.push_back(ln1[1]);
11956               ln.push_back(ln1[2]);
11957               ln.push_back(ln1[3]);
11958               break;
11959             case VTK_QUADRATIC_TRIANGLE:
11960               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11961                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11962               // MESSAGE("vol quad prism " << vol->GetID());
11963               ln.push_back(ln1[0]);
11964               ln.push_back(ln1[1]);
11965               ln.push_back(ln1[2]);
11966               ln.push_back(ln3[0]);
11967               ln.push_back(ln3[1]);
11968               ln.push_back(ln3[2]);
11969               break;
11970             case VTK_QUADRATIC_QUAD:
11971 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11972 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11973 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11974               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11975                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11976                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11977               // MESSAGE("vol quad hexa " << vol->GetID());
11978               ln.push_back(ln1[0]);
11979               ln.push_back(ln1[1]);
11980               ln.push_back(ln1[2]);
11981               ln.push_back(ln1[3]);
11982               ln.push_back(ln3[0]);
11983               ln.push_back(ln3[1]);
11984               ln.push_back(ln3[2]);
11985               ln.push_back(ln3[3]);
11986               break;
11987             case VTK_POLYGON:
11988               break;
11989             default:
11990               break;
11991           }
11992
11993           if (vol)
11994             {
11995               stringstream grpname;
11996               grpname << "jf_";
11997               grpname << idom;
11998               int idg;
11999               string namegrp = grpname.str();
12000               if (!mapOfJunctionGroups.count(namegrp))
12001                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12002               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12003               if (sgrp)
12004                 sgrp->Add(vol->GetID());
12005             }
12006
12007           // --- modify the face
12008
12009           aFace->ChangeNodes(&ln[0], ln.size());
12010         }
12011     }
12012   return true;
12013 }
12014
12015 /*!
12016  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12017  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12018  *  groups of faces to remove inside the object, (idem edges).
12019  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12020  */
12021 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12022                                       const TopoDS_Shape& theShape,
12023                                       SMESH_NodeSearcher* theNodeSearcher,
12024                                       const char* groupName,
12025                                       std::vector<double>&   nodesCoords,
12026                                       std::vector<std::vector<int> >& listOfListOfNodes)
12027 {
12028   MESSAGE("--------------------------------");
12029   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12030   MESSAGE("--------------------------------");
12031
12032   // --- zone of volumes to remove is given :
12033   //     1 either by a geom shape (one or more vertices) and a radius,
12034   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12035   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12036   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12037   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12038   //     defined by it's name.
12039
12040   SMESHDS_GroupBase* groupDS = 0;
12041   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12042   while ( groupIt->more() )
12043     {
12044       groupDS = 0;
12045       SMESH_Group * group = groupIt->next();
12046       if ( !group ) continue;
12047       groupDS = group->GetGroupDS();
12048       if ( !groupDS || groupDS->IsEmpty() ) continue;
12049       std::string grpName = group->GetName();
12050       //MESSAGE("grpName=" << grpName);
12051       if (grpName == groupName)
12052         break;
12053       else
12054         groupDS = 0;
12055     }
12056
12057   bool isNodeGroup = false;
12058   bool isNodeCoords = false;
12059   if (groupDS)
12060     {
12061       if (groupDS->GetType() != SMDSAbs_Node)
12062         return;
12063       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12064     }
12065
12066   if (nodesCoords.size() > 0)
12067     isNodeCoords = true; // a list o nodes given by their coordinates
12068   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12069
12070   // --- define groups to build
12071
12072   int idg; // --- group of SMDS volumes
12073   string grpvName = groupName;
12074   grpvName += "_vol";
12075   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12076   if (!grp)
12077     {
12078       MESSAGE("group not created " << grpvName);
12079       return;
12080     }
12081   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12082
12083   int idgs; // --- group of SMDS faces on the skin
12084   string grpsName = groupName;
12085   grpsName += "_skin";
12086   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12087   if (!grps)
12088     {
12089       MESSAGE("group not created " << grpsName);
12090       return;
12091     }
12092   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12093
12094   int idgi; // --- group of SMDS faces internal (several shapes)
12095   string grpiName = groupName;
12096   grpiName += "_internalFaces";
12097   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12098   if (!grpi)
12099     {
12100       MESSAGE("group not created " << grpiName);
12101       return;
12102     }
12103   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12104
12105   int idgei; // --- group of SMDS faces internal (several shapes)
12106   string grpeiName = groupName;
12107   grpeiName += "_internalEdges";
12108   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12109   if (!grpei)
12110     {
12111       MESSAGE("group not created " << grpeiName);
12112       return;
12113     }
12114   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12115
12116   // --- build downward connectivity
12117
12118   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12119   meshDS->BuildDownWardConnectivity(true);
12120   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12121
12122   // --- set of volumes detected inside
12123
12124   std::set<int> setOfInsideVol;
12125   std::set<int> setOfVolToCheck;
12126
12127   std::vector<gp_Pnt> gpnts;
12128   gpnts.clear();
12129
12130   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12131     {
12132       MESSAGE("group of nodes provided");
12133       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12134       while ( elemIt->more() )
12135         {
12136           const SMDS_MeshElement* elem = elemIt->next();
12137           if (!elem)
12138             continue;
12139           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12140           if (!node)
12141             continue;
12142           SMDS_MeshElement* vol = 0;
12143           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12144           while (volItr->more())
12145             {
12146               vol = (SMDS_MeshElement*)volItr->next();
12147               setOfInsideVol.insert(vol->getVtkId());
12148               sgrp->Add(vol->GetID());
12149             }
12150         }
12151     }
12152   else if (isNodeCoords)
12153     {
12154       MESSAGE("list of nodes coordinates provided");
12155       int i = 0;
12156       int k = 0;
12157       while (i < nodesCoords.size()-2)
12158         {
12159           double x = nodesCoords[i++];
12160           double y = nodesCoords[i++];
12161           double z = nodesCoords[i++];
12162           gp_Pnt p = gp_Pnt(x, y ,z);
12163           gpnts.push_back(p);
12164           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12165         }
12166     }
12167   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12168     {
12169       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12170       TopTools_IndexedMapOfShape vertexMap;
12171       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12172       gp_Pnt p = gp_Pnt(0,0,0);
12173       if (vertexMap.Extent() < 1)
12174         return;
12175
12176       for ( int i = 1; i <= vertexMap.Extent(); ++i )
12177         {
12178           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12179           p = BRep_Tool::Pnt(vertex);
12180           gpnts.push_back(p);
12181           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12182         }
12183     }
12184
12185   if (gpnts.size() > 0)
12186     {
12187       int nodeId = 0;
12188       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12189       if (startNode)
12190         nodeId = startNode->GetID();
12191       MESSAGE("nodeId " << nodeId);
12192
12193       double radius2 = radius*radius;
12194       MESSAGE("radius2 " << radius2);
12195
12196       // --- volumes on start node
12197
12198       setOfVolToCheck.clear();
12199       SMDS_MeshElement* startVol = 0;
12200       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12201       while (volItr->more())
12202         {
12203           startVol = (SMDS_MeshElement*)volItr->next();
12204           setOfVolToCheck.insert(startVol->getVtkId());
12205         }
12206       if (setOfVolToCheck.empty())
12207         {
12208           MESSAGE("No volumes found");
12209           return;
12210         }
12211
12212       // --- starting with central volumes then their neighbors, check if they are inside
12213       //     or outside the domain, until no more new neighbor volume is inside.
12214       //     Fill the group of inside volumes
12215
12216       std::map<int, double> mapOfNodeDistance2;
12217       mapOfNodeDistance2.clear();
12218       std::set<int> setOfOutsideVol;
12219       while (!setOfVolToCheck.empty())
12220         {
12221           std::set<int>::iterator it = setOfVolToCheck.begin();
12222           int vtkId = *it;
12223           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12224           bool volInside = false;
12225           vtkIdType npts = 0;
12226           vtkIdType* pts = 0;
12227           grid->GetCellPoints(vtkId, npts, pts);
12228           for (int i=0; i<npts; i++)
12229             {
12230               double distance2 = 0;
12231               if (mapOfNodeDistance2.count(pts[i]))
12232                 {
12233                   distance2 = mapOfNodeDistance2[pts[i]];
12234                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12235                 }
12236               else
12237                 {
12238                   double *coords = grid->GetPoint(pts[i]);
12239                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12240                   distance2 = 1.E40;
12241                   for (int j=0; j<gpnts.size(); j++)
12242                     {
12243                       double d2 = aPoint.SquareDistance(gpnts[j]);
12244                       if (d2 < distance2)
12245                         {
12246                           distance2 = d2;
12247                           if (distance2 < radius2)
12248                             break;
12249                         }
12250                     }
12251                   mapOfNodeDistance2[pts[i]] = distance2;
12252                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12253                 }
12254               if (distance2 < radius2)
12255                 {
12256                   volInside = true; // one or more nodes inside the domain
12257                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12258                   break;
12259                 }
12260             }
12261           if (volInside)
12262             {
12263               setOfInsideVol.insert(vtkId);
12264               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12265               int neighborsVtkIds[NBMAXNEIGHBORS];
12266               int downIds[NBMAXNEIGHBORS];
12267               unsigned char downTypes[NBMAXNEIGHBORS];
12268               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12269               for (int n = 0; n < nbNeighbors; n++)
12270                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12271                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12272             }
12273           else
12274             {
12275               setOfOutsideVol.insert(vtkId);
12276               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12277             }
12278           setOfVolToCheck.erase(vtkId);
12279         }
12280     }
12281
12282   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12283   //     If yes, add the volume to the inside set
12284
12285   bool addedInside = true;
12286   std::set<int> setOfVolToReCheck;
12287   while (addedInside)
12288     {
12289       MESSAGE(" --------------------------- re check");
12290       addedInside = false;
12291       std::set<int>::iterator itv = setOfInsideVol.begin();
12292       for (; itv != setOfInsideVol.end(); ++itv)
12293         {
12294           int vtkId = *itv;
12295           int neighborsVtkIds[NBMAXNEIGHBORS];
12296           int downIds[NBMAXNEIGHBORS];
12297           unsigned char downTypes[NBMAXNEIGHBORS];
12298           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12299           for (int n = 0; n < nbNeighbors; n++)
12300             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12301               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12302         }
12303       setOfVolToCheck = setOfVolToReCheck;
12304       setOfVolToReCheck.clear();
12305       while  (!setOfVolToCheck.empty())
12306         {
12307           std::set<int>::iterator it = setOfVolToCheck.begin();
12308           int vtkId = *it;
12309           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12310             {
12311               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12312               int countInside = 0;
12313               int neighborsVtkIds[NBMAXNEIGHBORS];
12314               int downIds[NBMAXNEIGHBORS];
12315               unsigned char downTypes[NBMAXNEIGHBORS];
12316               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12317               for (int n = 0; n < nbNeighbors; n++)
12318                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12319                   countInside++;
12320               MESSAGE("countInside " << countInside);
12321               if (countInside > 1)
12322                 {
12323                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12324                   setOfInsideVol.insert(vtkId);
12325                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12326                   addedInside = true;
12327                 }
12328               else
12329                 setOfVolToReCheck.insert(vtkId);
12330             }
12331           setOfVolToCheck.erase(vtkId);
12332         }
12333     }
12334
12335   // --- map of Downward faces at the boundary, inside the global volume
12336   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12337   //     fill group of SMDS faces inside the volume (when several volume shapes)
12338   //     fill group of SMDS faces on the skin of the global volume (if skin)
12339
12340   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12341   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12342   std::set<int>::iterator it = setOfInsideVol.begin();
12343   for (; it != setOfInsideVol.end(); ++it)
12344     {
12345       int vtkId = *it;
12346       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12347       int neighborsVtkIds[NBMAXNEIGHBORS];
12348       int downIds[NBMAXNEIGHBORS];
12349       unsigned char downTypes[NBMAXNEIGHBORS];
12350       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12351       for (int n = 0; n < nbNeighbors; n++)
12352         {
12353           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12354           if (neighborDim == 3)
12355             {
12356               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12357                 {
12358                   DownIdType face(downIds[n], downTypes[n]);
12359                   boundaryFaces[face] = vtkId;
12360                 }
12361               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12362               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12363               if (vtkFaceId >= 0)
12364                 {
12365                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12366                   // find also the smds edges on this face
12367                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12368                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12369                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12370                   for (int i = 0; i < nbEdges; i++)
12371                     {
12372                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12373                       if (vtkEdgeId >= 0)
12374                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12375                     }
12376                 }
12377             }
12378           else if (neighborDim == 2) // skin of the volume
12379             {
12380               DownIdType face(downIds[n], downTypes[n]);
12381               skinFaces[face] = vtkId;
12382               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12383               if (vtkFaceId >= 0)
12384                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12385             }
12386         }
12387     }
12388
12389   // --- identify the edges constituting the wire of each subshape on the skin
12390   //     define polylines with the nodes of edges, equivalent to wires
12391   //     project polylines on subshapes, and partition, to get geom faces
12392
12393   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12394   std::set<int> emptySet;
12395   emptySet.clear();
12396   std::set<int> shapeIds;
12397
12398   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12399   while (itelem->more())
12400     {
12401       const SMDS_MeshElement *elem = itelem->next();
12402       int shapeId = elem->getshapeId();
12403       int vtkId = elem->getVtkId();
12404       if (!shapeIdToVtkIdSet.count(shapeId))
12405         {
12406           shapeIdToVtkIdSet[shapeId] = emptySet;
12407           shapeIds.insert(shapeId);
12408         }
12409       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12410     }
12411
12412   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12413   std::set<DownIdType, DownIdCompare> emptyEdges;
12414   emptyEdges.clear();
12415
12416   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12417   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12418     {
12419       int shapeId = itShape->first;
12420       MESSAGE(" --- Shape ID --- "<< shapeId);
12421       shapeIdToEdges[shapeId] = emptyEdges;
12422
12423       std::vector<int> nodesEdges;
12424
12425       std::set<int>::iterator its = itShape->second.begin();
12426       for (; its != itShape->second.end(); ++its)
12427         {
12428           int vtkId = *its;
12429           MESSAGE("     " << vtkId);
12430           int neighborsVtkIds[NBMAXNEIGHBORS];
12431           int downIds[NBMAXNEIGHBORS];
12432           unsigned char downTypes[NBMAXNEIGHBORS];
12433           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12434           for (int n = 0; n < nbNeighbors; n++)
12435             {
12436               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12437                 continue;
12438               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12439               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12440               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12441                 {
12442                   DownIdType edge(downIds[n], downTypes[n]);
12443                   if (!shapeIdToEdges[shapeId].count(edge))
12444                     {
12445                       shapeIdToEdges[shapeId].insert(edge);
12446                       int vtkNodeId[3];
12447                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12448                       nodesEdges.push_back(vtkNodeId[0]);
12449                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12450                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12451                     }
12452                 }
12453             }
12454         }
12455
12456       std::list<int> order;
12457       order.clear();
12458       if (nodesEdges.size() > 0)
12459         {
12460           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12461           nodesEdges[0] = -1;
12462           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12463           nodesEdges[1] = -1; // do not reuse this edge
12464           bool found = true;
12465           while (found)
12466             {
12467               int nodeTofind = order.back(); // try first to push back
12468               int i = 0;
12469               for (i = 0; i<nodesEdges.size(); i++)
12470                 if (nodesEdges[i] == nodeTofind)
12471                   break;
12472               if (i == nodesEdges.size())
12473                 found = false; // no follower found on back
12474               else
12475                 {
12476                   if (i%2) // odd ==> use the previous one
12477                     if (nodesEdges[i-1] < 0)
12478                       found = false;
12479                     else
12480                       {
12481                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12482                         nodesEdges[i-1] = -1;
12483                       }
12484                   else // even ==> use the next one
12485                     if (nodesEdges[i+1] < 0)
12486                       found = false;
12487                     else
12488                       {
12489                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12490                         nodesEdges[i+1] = -1;
12491                       }
12492                 }
12493               if (found)
12494                 continue;
12495               // try to push front
12496               found = true;
12497               nodeTofind = order.front(); // try to push front
12498               for (i = 0; i<nodesEdges.size(); i++)
12499                 if (nodesEdges[i] == nodeTofind)
12500                   break;
12501               if (i == nodesEdges.size())
12502                 {
12503                   found = false; // no predecessor found on front
12504                   continue;
12505                 }
12506               if (i%2) // odd ==> use the previous one
12507                 if (nodesEdges[i-1] < 0)
12508                   found = false;
12509                 else
12510                   {
12511                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12512                     nodesEdges[i-1] = -1;
12513                   }
12514               else // even ==> use the next one
12515                 if (nodesEdges[i+1] < 0)
12516                   found = false;
12517                 else
12518                   {
12519                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12520                     nodesEdges[i+1] = -1;
12521                   }
12522             }
12523         }
12524
12525
12526       std::vector<int> nodes;
12527       nodes.push_back(shapeId);
12528       std::list<int>::iterator itl = order.begin();
12529       for (; itl != order.end(); itl++)
12530         {
12531           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12532           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12533         }
12534       listOfListOfNodes.push_back(nodes);
12535     }
12536
12537   //     partition geom faces with blocFissure
12538   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12539   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12540
12541   return;
12542 }
12543
12544
12545 //================================================================================
12546 /*!
12547  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12548  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12549  * \return TRUE if operation has been completed successfully, FALSE otherwise
12550  */
12551 //================================================================================
12552
12553 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12554 {
12555   // iterates on volume elements and detect all free faces on them
12556   SMESHDS_Mesh* aMesh = GetMeshDS();
12557   if (!aMesh)
12558     return false;
12559   //bool res = false;
12560   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12561   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12562   while(vIt->more())
12563   {
12564     const SMDS_MeshVolume* volume = vIt->next();
12565     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12566     vTool.SetExternalNormal();
12567     //const bool isPoly = volume->IsPoly();
12568     const int iQuad = volume->IsQuadratic();
12569     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12570     {
12571       if (!vTool.IsFreeFace(iface))
12572         continue;
12573       nbFree++;
12574       vector<const SMDS_MeshNode *> nodes;
12575       int nbFaceNodes = vTool.NbFaceNodes(iface);
12576       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12577       int inode = 0;
12578       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12579         nodes.push_back(faceNodes[inode]);
12580       if (iQuad) { // add medium nodes
12581         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12582           nodes.push_back(faceNodes[inode]);
12583         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12584           nodes.push_back(faceNodes[8]);
12585       }
12586       // add new face based on volume nodes
12587       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12588         nbExisted++;
12589         continue; // face already exsist
12590       }
12591       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12592       nbCreated++;
12593     }
12594   }
12595   return ( nbFree==(nbExisted+nbCreated) );
12596 }
12597
12598 namespace
12599 {
12600   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12601   {
12602     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12603       return n;
12604     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12605   }
12606 }
12607 //================================================================================
12608 /*!
12609  * \brief Creates missing boundary elements
12610  *  \param elements - elements whose boundary is to be checked
12611  *  \param dimension - defines type of boundary elements to create
12612  *  \param group - a group to store created boundary elements in
12613  *  \param targetMesh - a mesh to store created boundary elements in
12614  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12615  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12616  *                                boundary elements will be copied into the targetMesh
12617  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12618  *                                boundary elements will be added into the new group
12619  *  \param aroundElements - if true, elements will be created on boundary of given
12620  *                          elements else, on boundary of the whole mesh.
12621  * \return nb of added boundary elements
12622  */
12623 //================================================================================
12624
12625 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12626                                        Bnd_Dimension           dimension,
12627                                        SMESH_Group*            group/*=0*/,
12628                                        SMESH_Mesh*             targetMesh/*=0*/,
12629                                        bool                    toCopyElements/*=false*/,
12630                                        bool                    toCopyExistingBoundary/*=false*/,
12631                                        bool                    toAddExistingBondary/*= false*/,
12632                                        bool                    aroundElements/*= false*/)
12633 {
12634   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12635   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12636   // hope that all elements are of the same type, do not check them all
12637   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12638     throw SALOME_Exception(LOCALIZED("wrong element type"));
12639
12640   if ( !targetMesh )
12641     toCopyElements = toCopyExistingBoundary = false;
12642
12643   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12644   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12645   int nbAddedBnd = 0;
12646
12647   // editor adding present bnd elements and optionally holding elements to add to the group
12648   SMESH_MeshEditor* presentEditor;
12649   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12650   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12651
12652   SMESH_MesherHelper helper( *myMesh );
12653   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12654   SMDS_VolumeTool vTool;
12655   TIDSortedElemSet avoidSet;
12656   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12657   int inode;
12658
12659   typedef vector<const SMDS_MeshNode*> TConnectivity;
12660
12661   SMDS_ElemIteratorPtr eIt;
12662   if (elements.empty())
12663     eIt = aMesh->elementsIterator(elemType);
12664   else
12665     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12666
12667   while (eIt->more())
12668   {
12669     const SMDS_MeshElement* elem = eIt->next();
12670     const int iQuad = elem->IsQuadratic();
12671
12672     // ------------------------------------------------------------------------------------
12673     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12674     // ------------------------------------------------------------------------------------
12675     vector<const SMDS_MeshElement*> presentBndElems;
12676     vector<TConnectivity>           missingBndElems;
12677     TConnectivity nodes;
12678     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12679     {
12680       vTool.SetExternalNormal();
12681       const SMDS_MeshElement* otherVol = 0;
12682       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12683       {
12684         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12685              ( !aroundElements || elements.count( otherVol )))
12686           continue;
12687         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12688         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12689         if ( missType == SMDSAbs_Edge ) // boundary edges
12690         {
12691           nodes.resize( 2+iQuad );
12692           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12693           {
12694             for ( int j = 0; j < nodes.size(); ++j )
12695               nodes[j] =nn[i+j];
12696             if ( const SMDS_MeshElement* edge =
12697                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12698               presentBndElems.push_back( edge );
12699             else
12700               missingBndElems.push_back( nodes );
12701           }
12702         }
12703         else // boundary face
12704         {
12705           nodes.clear();
12706           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12707             nodes.push_back( nn[inode] );
12708           if (iQuad) // add medium nodes
12709             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12710               nodes.push_back( nn[inode] );
12711           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12712           if ( iCenter > 0 )
12713             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12714
12715           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12716                                                                SMDSAbs_Face, /*noMedium=*/false ))
12717             presentBndElems.push_back( f );
12718           else
12719             missingBndElems.push_back( nodes );
12720
12721           if ( targetMesh != myMesh )
12722           {
12723             // add 1D elements on face boundary to be added to a new mesh
12724             const SMDS_MeshElement* edge;
12725             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12726             {
12727               if ( iQuad )
12728                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12729               else
12730                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12731               if ( edge && avoidSet.insert( edge ).second )
12732                 presentBndElems.push_back( edge );
12733             }
12734           }
12735         }
12736       }
12737     }
12738     else                     // elem is a face ------------------------------------------
12739     {
12740       avoidSet.clear(), avoidSet.insert( elem );
12741       int nbNodes = elem->NbCornerNodes();
12742       nodes.resize( 2 /*+ iQuad*/);
12743       for ( int i = 0; i < nbNodes; i++ )
12744       {
12745         nodes[0] = elem->GetNode(i);
12746         nodes[1] = elem->GetNode((i+1)%nbNodes);
12747         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12748           continue; // not free link
12749
12750         //if ( iQuad )
12751         //nodes[2] = elem->GetNode( i + nbNodes );
12752         if ( const SMDS_MeshElement* edge =
12753              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12754           presentBndElems.push_back( edge );
12755         else
12756           missingBndElems.push_back( nodes );
12757       }
12758     }
12759
12760     // ---------------------------------
12761     // 2. Add missing boundary elements
12762     // ---------------------------------
12763     if ( targetMesh != myMesh )
12764       // instead of making a map of nodes in this mesh and targetMesh,
12765       // we create nodes with same IDs.
12766       for ( int i = 0; i < missingBndElems.size(); ++i )
12767       {
12768         TConnectivity& srcNodes = missingBndElems[i];
12769         TConnectivity  nodes( srcNodes.size() );
12770         for ( inode = 0; inode < nodes.size(); ++inode )
12771           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12772         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12773                                                                    missType,
12774                                                                    /*noMedium=*/false))
12775           continue;
12776         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12777         ++nbAddedBnd;
12778       }
12779     else
12780       for ( int i = 0; i < missingBndElems.size(); ++i )
12781       {
12782         TConnectivity& nodes = missingBndElems[i];
12783         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12784                                                                    missType,
12785                                                                    /*noMedium=*/false))
12786           continue;
12787         SMDS_MeshElement* elem =
12788           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12789         ++nbAddedBnd;
12790
12791         // try to set a new element to a shape
12792         if ( myMesh->HasShapeToMesh() )
12793         {
12794           bool ok = true;
12795           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12796           const int nbN = nodes.size() / (iQuad+1 );
12797           for ( inode = 0; inode < nbN && ok; ++inode )
12798           {
12799             pair<int, TopAbs_ShapeEnum> i_stype =
12800               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12801             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12802               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12803           }
12804           if ( ok && mediumShapes.size() > 1 )
12805           {
12806             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12807             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12808             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12809             {
12810               if (( ok = ( stype_i->first != stype_i_0.first )))
12811                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12812                                         aMesh->IndexToShape( stype_i_0.second ));
12813             }
12814           }
12815           if ( ok && mediumShapes.begin()->first == missShapeType )
12816             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12817         }
12818       }
12819
12820     // ----------------------------------
12821     // 3. Copy present boundary elements
12822     // ----------------------------------
12823     if ( toCopyExistingBoundary )
12824       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12825       {
12826         const SMDS_MeshElement* e = presentBndElems[i];
12827         TConnectivity nodes( e->NbNodes() );
12828         for ( inode = 0; inode < nodes.size(); ++inode )
12829           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12830         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12831       }
12832     else // store present elements to add them to a group
12833       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12834       {
12835         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12836       }
12837
12838   } // loop on given elements
12839
12840   // ---------------------------------------------
12841   // 4. Fill group with boundary elements
12842   // ---------------------------------------------
12843   if ( group )
12844   {
12845     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12846       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12847         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12848   }
12849   tgtEditor.myLastCreatedElems.Clear();
12850   tgtEditor2.myLastCreatedElems.Clear();
12851
12852   // -----------------------
12853   // 5. Copy given elements
12854   // -----------------------
12855   if ( toCopyElements && targetMesh != myMesh )
12856   {
12857     if (elements.empty())
12858       eIt = aMesh->elementsIterator(elemType);
12859     else
12860       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12861     while (eIt->more())
12862     {
12863       const SMDS_MeshElement* elem = eIt->next();
12864       TConnectivity nodes( elem->NbNodes() );
12865       for ( inode = 0; inode < nodes.size(); ++inode )
12866         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12867       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12868
12869       tgtEditor.myLastCreatedElems.Clear();
12870     }
12871   }
12872   return nbAddedBnd;
12873 }