Salome HOME
52426, 22582: EDF 8036 SMESH: ConvertToQuadratic fails with theForce3d off
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Face.hxx>
76 #include <TopoDS_Solid.hxx>
77 #include <gp.hxx>
78 #include <gp_Ax1.hxx>
79 #include <gp_Dir.hxx>
80 #include <gp_Lin.hxx>
81 #include <gp_Pln.hxx>
82 #include <gp_Trsf.hxx>
83 #include <gp_Vec.hxx>
84 #include <gp_XY.hxx>
85 #include <gp_XYZ.hxx>
86
87 #include <cmath>
88
89 #include <map>
90 #include <set>
91 #include <numeric>
92 #include <limits>
93 #include <algorithm>
94 #include <sstream>
95
96 #include <boost/tuple/tuple.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
102
103 using namespace std;
104 using namespace SMESH::Controls;
105
106 namespace
107 {
108   template < class ELEM_SET >
109   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
110   {
111     typedef SMDS_SetIterator
112       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
114   }
115 }
116
117 //=======================================================================
118 //function : SMESH_MeshEditor
119 //purpose  :
120 //=======================================================================
121
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123   :myMesh( theMesh ) // theMesh may be NULL
124 {
125 }
126
127 //================================================================================
128 /*!
129  * \brief Clears myLastCreatedNodes and myLastCreatedElems
130  */
131 //================================================================================
132
133 void SMESH_MeshEditor::CrearLastCreated()
134 {
135   myLastCreatedNodes.Clear();
136   myLastCreatedElems.Clear();
137 }
138
139
140 //=======================================================================
141 /*!
142  * \brief Add element
143  */
144 //=======================================================================
145
146 SMDS_MeshElement*
147 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
148                              const SMDSAbs_ElementType            type,
149                              const bool                           isPoly,
150                              const int                            ID,
151                              const double                         ballDiameter)
152 {
153   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
154   SMDS_MeshElement* e = 0;
155   int nbnode = node.size();
156   SMESHDS_Mesh* mesh = GetMeshDS();
157   switch ( type ) {
158   case SMDSAbs_Face:
159     if ( !isPoly ) {
160       if      (nbnode == 3) {
161         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
162         else           e = mesh->AddFace      (node[0], node[1], node[2] );
163       }
164       else if (nbnode == 4) {
165         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
166         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
167       }
168       else if (nbnode == 6) {
169         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
170                                                node[4], node[5], ID);
171         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
172                                                node[4], node[5] );
173       }
174       else if (nbnode == 7) {
175         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
176                                                node[4], node[5], node[6], ID);
177         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
178                                                node[4], node[5], node[6] );
179       }
180       else if (nbnode == 8) {
181         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
182                                                node[4], node[5], node[6], node[7], ID);
183         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
184                                                node[4], node[5], node[6], node[7] );
185       }
186       else if (nbnode == 9) {
187         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
188                                                node[4], node[5], node[6], node[7], node[8], ID);
189         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
190                                                node[4], node[5], node[6], node[7], node[8] );
191       }
192     } else {
193       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
194       else           e = mesh->AddPolygonalFace      (node    );
195     }
196     break;
197
198   case SMDSAbs_Volume:
199     if ( !isPoly ) {
200       if      (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 5) {
205         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
206                                                  node[4], ID);
207         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
208                                                  node[4] );
209       }
210       else if (nbnode == 6) {
211         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], ID);
213         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
214                                                  node[4], node[5] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
218                                                  node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
220                                                  node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 10) {
223         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224                                                  node[4], node[5], node[6], node[7],
225                                                  node[8], node[9], ID);
226         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
227                                                  node[4], node[5], node[6], node[7],
228                                                  node[8], node[9] );
229       }
230       else if (nbnode == 12) {
231         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232                                                  node[4], node[5], node[6], node[7],
233                                                  node[8], node[9], node[10], node[11], ID);
234         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
235                                                  node[4], node[5], node[6], node[7],
236                                                  node[8], node[9], node[10], node[11] );
237       }
238       else if (nbnode == 13) {
239         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240                                                  node[4], node[5], node[6], node[7],
241                                                  node[8], node[9], node[10],node[11],
242                                                  node[12],ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
244                                                  node[4], node[5], node[6], node[7],
245                                                  node[8], node[9], node[10],node[11],
246                                                  node[12] );
247       }
248       else if (nbnode == 15) {
249         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250                                                  node[4], node[5], node[6], node[7],
251                                                  node[8], node[9], node[10],node[11],
252                                                  node[12],node[13],node[14],ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], node[6], node[7],
255                                                  node[8], node[9], node[10],node[11],
256                                                  node[12],node[13],node[14] );
257       }
258       else if (nbnode == 20) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7],
261                                                  node[8], node[9], node[10],node[11],
262                                                  node[12],node[13],node[14],node[15],
263                                                  node[16],node[17],node[18],node[19],ID);
264         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], node[10],node[11],
267                                                  node[12],node[13],node[14],node[15],
268                                                  node[16],node[17],node[18],node[19] );
269       }
270       else if (nbnode == 27) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9], node[10],node[11],
274                                                  node[12],node[13],node[14],node[15],
275                                                  node[16],node[17],node[18],node[19],
276                                                  node[20],node[21],node[22],node[23],
277                                                  node[24],node[25],node[26], ID);
278         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9], node[10],node[11],
281                                                  node[12],node[13],node[14],node[15],
282                                                  node[16],node[17],node[18],node[19],
283                                                  node[20],node[21],node[22],node[23],
284                                                  node[24],node[25],node[26] );
285       }
286     }
287     break;
288
289   case SMDSAbs_Edge:
290     if ( nbnode == 2 ) {
291       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
292       else           e = mesh->AddEdge      (node[0], node[1] );
293     }
294     else if ( nbnode == 3 ) {
295       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
296       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
297     }
298     break;
299
300   case SMDSAbs_0DElement:
301     if ( nbnode == 1 ) {
302       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
303       else           e = mesh->Add0DElement      (node[0] );
304     }
305     break;
306
307   case SMDSAbs_Node:
308     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
309     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
310     break;
311
312   case SMDSAbs_Ball:
313     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
314     else           e = mesh->AddBall      (node[0], ballDiameter);
315     break;
316
317   default:;
318   }
319   if ( e ) myLastCreatedElems.Append( e );
320   return e;
321 }
322
323 //=======================================================================
324 /*!
325  * \brief Add element
326  */
327 //=======================================================================
328
329 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
330                                                const SMDSAbs_ElementType type,
331                                                const bool                isPoly,
332                                                const int                 ID)
333 {
334   vector<const SMDS_MeshNode*> nodes;
335   nodes.reserve( nodeIDs.size() );
336   vector<int>::const_iterator id = nodeIDs.begin();
337   while ( id != nodeIDs.end() ) {
338     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
339       nodes.push_back( node );
340     else
341       return 0;
342   }
343   return AddElement( nodes, type, isPoly, ID );
344 }
345
346 //=======================================================================
347 //function : Remove
348 //purpose  : Remove a node or an element.
349 //           Modify a compute state of sub-meshes which become empty
350 //=======================================================================
351
352 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
353                               const bool         isNodes )
354 {
355   myLastCreatedElems.Clear();
356   myLastCreatedNodes.Clear();
357
358   SMESHDS_Mesh* aMesh = GetMeshDS();
359   set< SMESH_subMesh *> smmap;
360
361   int removed = 0;
362   list<int>::const_iterator it = theIDs.begin();
363   for ( ; it != theIDs.end(); it++ ) {
364     const SMDS_MeshElement * elem;
365     if ( isNodes )
366       elem = aMesh->FindNode( *it );
367     else
368       elem = aMesh->FindElement( *it );
369     if ( !elem )
370       continue;
371
372     // Notify VERTEX sub-meshes about modification
373     if ( isNodes ) {
374       const SMDS_MeshNode* node = cast2Node( elem );
375       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
376         if ( int aShapeID = node->getshapeId() )
377           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378             smmap.insert( sm );
379     }
380     // Find sub-meshes to notify about modification
381     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
382     //     while ( nodeIt->more() ) {
383     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
384     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
385     //       if ( aPosition.get() ) {
386     //         if ( int aShapeID = aPosition->GetShapeId() ) {
387     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
388     //             smmap.insert( sm );
389     //         }
390     //       }
391     //     }
392
393     // Do remove
394     if ( isNodes )
395       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
396     else
397       aMesh->RemoveElement( elem );
398     removed++;
399   }
400
401   // Notify sub-meshes about modification
402   if ( !smmap.empty() ) {
403     set< SMESH_subMesh *>::iterator smIt;
404     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
405       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
406   }
407
408   //   // Check if the whole mesh becomes empty
409   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
410   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
411
412   return removed;
413 }
414
415 //================================================================================
416 /*!
417  * \brief Create 0D elements on all nodes of the given object except those
418  *        nodes on which a 0D element already exists.
419  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
420  *                    the all mesh is treated
421  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
422  */
423 //================================================================================
424
425 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
426                                                    TIDSortedElemSet&       all0DElems )
427 {
428   SMDS_ElemIteratorPtr elemIt;
429   vector< const SMDS_MeshElement* > allNodes;
430   if ( elements.empty() )
431   {
432     allNodes.reserve( GetMeshDS()->NbNodes() );
433     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
434     while ( elemIt->more() )
435       allNodes.push_back( elemIt->next() );
436
437     elemIt = elemSetIterator( allNodes );
438   }
439   else
440   {
441     elemIt = elemSetIterator( elements );
442   }
443
444   while ( elemIt->more() )
445   {
446     const SMDS_MeshElement* e = elemIt->next();
447     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
448     while ( nodeIt->more() )
449     {
450       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
451       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
452       if ( it0D->more() )
453         all0DElems.insert( it0D->next() );
454       else {
455         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
456         all0DElems.insert( myLastCreatedElems.Last() );
457       }
458     }
459   }
460 }
461
462 //=======================================================================
463 //function : FindShape
464 //purpose  : Return an index of the shape theElem is on
465 //           or zero if a shape not found
466 //=======================================================================
467
468 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
469 {
470   myLastCreatedElems.Clear();
471   myLastCreatedNodes.Clear();
472
473   SMESHDS_Mesh * aMesh = GetMeshDS();
474   if ( aMesh->ShapeToMesh().IsNull() )
475     return 0;
476
477   int aShapeID = theElem->getshapeId();
478   if ( aShapeID < 1 )
479     return 0;
480
481   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
482     if ( sm->Contains( theElem ))
483       return aShapeID;
484
485   if ( theElem->GetType() == SMDSAbs_Node ) {
486     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
487   }
488   else {
489     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
490   }
491
492   TopoDS_Shape aShape; // the shape a node of theElem is on
493   if ( theElem->GetType() != SMDSAbs_Node )
494   {
495     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
496     while ( nodeIt->more() ) {
497       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
498       if ((aShapeID = node->getshapeId()) > 0) {
499         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
500           if ( sm->Contains( theElem ))
501             return aShapeID;
502           if ( aShape.IsNull() )
503             aShape = aMesh->IndexToShape( aShapeID );
504         }
505       }
506     }
507   }
508
509   // None of nodes is on a proper shape,
510   // find the shape among ancestors of aShape on which a node is
511   if ( !aShape.IsNull() ) {
512     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
513     for ( ; ancIt.More(); ancIt.Next() ) {
514       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
515       if ( sm && sm->Contains( theElem ))
516         return aMesh->ShapeToIndex( ancIt.Value() );
517     }
518   }
519   else
520   {
521     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
522     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
523     for ( ; id_sm != id2sm.end(); ++id_sm )
524       if ( id_sm->second->Contains( theElem ))
525         return id_sm->first;
526   }
527
528   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
529   return 0;
530 }
531
532 //=======================================================================
533 //function : IsMedium
534 //purpose  :
535 //=======================================================================
536
537 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
538                                 const SMDSAbs_ElementType typeToCheck)
539 {
540   bool isMedium = false;
541   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
542   while (it->more() && !isMedium ) {
543     const SMDS_MeshElement* elem = it->next();
544     isMedium = elem->IsMediumNode(node);
545   }
546   return isMedium;
547 }
548
549 //=======================================================================
550 //function : shiftNodesQuadTria
551 //purpose  : Shift nodes in the array corresponded to quadratic triangle
552 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
553 //=======================================================================
554
555 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
556 {
557   const SMDS_MeshNode* nd1 = aNodes[0];
558   aNodes[0] = aNodes[1];
559   aNodes[1] = aNodes[2];
560   aNodes[2] = nd1;
561   const SMDS_MeshNode* nd2 = aNodes[3];
562   aNodes[3] = aNodes[4];
563   aNodes[4] = aNodes[5];
564   aNodes[5] = nd2;
565 }
566
567 //=======================================================================
568 //function : nbEdgeConnectivity
569 //purpose  : return number of the edges connected with the theNode.
570 //           if theEdges has connections with the other type of the
571 //           elements, return -1
572 //=======================================================================
573
574 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
575 {
576   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
577   // int nb=0;
578   // while(elemIt->more()) {
579   //   elemIt->next();
580   //   nb++;
581   // }
582   // return nb;
583   return theNode->NbInverseElements();
584 }
585
586 //=======================================================================
587 //function : getNodesFromTwoTria
588 //purpose  : 
589 //=======================================================================
590
591 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
592                                 const SMDS_MeshElement * theTria2,
593                                 vector< const SMDS_MeshNode*>& N1,
594                                 vector< const SMDS_MeshNode*>& N2)
595 {
596   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
597   if ( N1.size() < 6 ) return false;
598   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
599   if ( N2.size() < 6 ) return false;
600
601   int sames[3] = {-1,-1,-1};
602   int nbsames = 0;
603   int i, j;
604   for(i=0; i<3; i++) {
605     for(j=0; j<3; j++) {
606       if(N1[i]==N2[j]) {
607         sames[i] = j;
608         nbsames++;
609         break;
610       }
611     }
612   }
613   if(nbsames!=2) return false;
614   if(sames[0]>-1) {
615     shiftNodesQuadTria(N1);
616     if(sames[1]>-1) {
617       shiftNodesQuadTria(N1);
618     }
619   }
620   i = sames[0] + sames[1] + sames[2];
621   for(; i<2; i++) {
622     shiftNodesQuadTria(N2);
623   }
624   // now we receive following N1 and N2 (using numeration as in the image below)
625   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
626   // i.e. first nodes from both arrays form a new diagonal
627   return true;
628 }
629
630 //=======================================================================
631 //function : InverseDiag
632 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
633 //           but having other common link.
634 //           Return False if args are improper
635 //=======================================================================
636
637 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
638                                     const SMDS_MeshElement * theTria2 )
639 {
640   MESSAGE("InverseDiag");
641   myLastCreatedElems.Clear();
642   myLastCreatedNodes.Clear();
643
644   if (!theTria1 || !theTria2)
645     return false;
646
647   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
648   if (!F1) return false;
649   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
650   if (!F2) return false;
651   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
652       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
653
654     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
655     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
656     //    |/ |                                         | \|
657     //  B +--+ 2                                     B +--+ 2
658
659     // put nodes in array and find out indices of the same ones
660     const SMDS_MeshNode* aNodes [6];
661     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
662     int i = 0;
663     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
664     while ( it->more() ) {
665       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
666
667       if ( i > 2 ) // theTria2
668         // find same node of theTria1
669         for ( int j = 0; j < 3; j++ )
670           if ( aNodes[ i ] == aNodes[ j ]) {
671             sameInd[ j ] = i;
672             sameInd[ i ] = j;
673             break;
674           }
675       // next
676       i++;
677       if ( i == 3 ) {
678         if ( it->more() )
679           return false; // theTria1 is not a triangle
680         it = theTria2->nodesIterator();
681       }
682       if ( i == 6 && it->more() )
683         return false; // theTria2 is not a triangle
684     }
685
686     // find indices of 1,2 and of A,B in theTria1
687     int iA = -1, iB = 0, i1 = 0, i2 = 0;
688     for ( i = 0; i < 6; i++ ) {
689       if ( sameInd [ i ] == -1 ) {
690         if ( i < 3 ) i1 = i;
691         else         i2 = i;
692       }
693       else if (i < 3) {
694         if ( iA >= 0) iB = i;
695         else          iA = i;
696       }
697     }
698     // nodes 1 and 2 should not be the same
699     if ( aNodes[ i1 ] == aNodes[ i2 ] )
700       return false;
701
702     // theTria1: A->2
703     aNodes[ iA ] = aNodes[ i2 ];
704     // theTria2: B->1
705     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
706
707     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
708     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
709
710     return true;
711
712   } // end if(F1 && F2)
713
714   // check case of quadratic faces
715   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
716       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
717     return false;
718   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
719       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
720     return false;
721
722   //       5
723   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
724   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
725   //    |   / |
726   //  7 +  +  + 6
727   //    | /9  |
728   //    |/    |
729   //  4 +--+--+ 3
730   //       8
731
732   vector< const SMDS_MeshNode* > N1;
733   vector< const SMDS_MeshNode* > N2;
734   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
735     return false;
736   // now we receive following N1 and N2 (using numeration as above image)
737   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
738   // i.e. first nodes from both arrays determ new diagonal
739
740   vector< const SMDS_MeshNode*> N1new( N1.size() );
741   vector< const SMDS_MeshNode*> N2new( N2.size() );
742   N1new.back() = N1.back(); // central node of biquadratic
743   N2new.back() = N2.back();
744   N1new[0] = N1[0];  N2new[0] = N1[0];
745   N1new[1] = N2[0];  N2new[1] = N1[1];
746   N1new[2] = N2[1];  N2new[2] = N2[0];
747   N1new[3] = N1[4];  N2new[3] = N1[3];
748   N1new[4] = N2[3];  N2new[4] = N2[5];
749   N1new[5] = N1[5];  N2new[5] = N1[4];
750   // change nodes in faces
751   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
752   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
753
754   // move the central node of biquadratic triangle
755   SMESH_MesherHelper helper( *GetMesh() );
756   for ( int is2nd = 0; is2nd < 2; ++is2nd )
757   {
758     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
759     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
760     if ( nodes.size() < 7 )
761       continue;
762     helper.SetSubShape( tria->getshapeId() );
763     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
764     gp_Pnt xyz;
765     if ( F.IsNull() )
766     {
767       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
768               SMESH_TNodeXYZ( nodes[4] ) +
769               SMESH_TNodeXYZ( nodes[5] )) / 3.;
770     }
771     else
772     {
773       bool checkUV;
774       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
775                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
776                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
777       TopLoc_Location loc;
778       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
779       xyz = S->Value( uv.X(), uv.Y() );
780       xyz.Transform( loc );
781       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
782            nodes[6]->getshapeId() > 0 )
783         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
784     }
785     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
786   }
787   return true;
788 }
789
790 //=======================================================================
791 //function : findTriangles
792 //purpose  : find triangles sharing theNode1-theNode2 link
793 //=======================================================================
794
795 static bool findTriangles(const SMDS_MeshNode *    theNode1,
796                           const SMDS_MeshNode *    theNode2,
797                           const SMDS_MeshElement*& theTria1,
798                           const SMDS_MeshElement*& theTria2)
799 {
800   if ( !theNode1 || !theNode2 ) return false;
801
802   theTria1 = theTria2 = 0;
803
804   set< const SMDS_MeshElement* > emap;
805   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
806   while (it->more()) {
807     const SMDS_MeshElement* elem = it->next();
808     if ( elem->NbCornerNodes() == 3 )
809       emap.insert( elem );
810   }
811   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
812   while (it->more()) {
813     const SMDS_MeshElement* elem = it->next();
814     if ( emap.count( elem )) {
815       if ( !theTria1 )
816       {
817         theTria1 = elem;
818       }
819       else  
820       {
821         theTria2 = elem;
822         // theTria1 must be element with minimum ID
823         if ( theTria2->GetID() < theTria1->GetID() )
824           std::swap( theTria2, theTria1 );
825         return true;
826       }
827     }
828   }
829   return false;
830 }
831
832 //=======================================================================
833 //function : InverseDiag
834 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
835 //           with ones built on the same 4 nodes but having other common link.
836 //           Return false if proper faces not found
837 //=======================================================================
838
839 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
840                                     const SMDS_MeshNode * theNode2)
841 {
842   myLastCreatedElems.Clear();
843   myLastCreatedNodes.Clear();
844
845   MESSAGE( "::InverseDiag()" );
846
847   const SMDS_MeshElement *tr1, *tr2;
848   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
849     return false;
850
851   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
852   if (!F1) return false;
853   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
854   if (!F2) return false;
855   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
856       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
857
858     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
859     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
860     //    |/ |                                    | \|
861     //  B +--+ 2                                B +--+ 2
862
863     // put nodes in array
864     // and find indices of 1,2 and of A in tr1 and of B in tr2
865     int i, iA1 = 0, i1 = 0;
866     const SMDS_MeshNode* aNodes1 [3];
867     SMDS_ElemIteratorPtr it;
868     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
869       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
870       if ( aNodes1[ i ] == theNode1 )
871         iA1 = i; // node A in tr1
872       else if ( aNodes1[ i ] != theNode2 )
873         i1 = i;  // node 1
874     }
875     int iB2 = 0, i2 = 0;
876     const SMDS_MeshNode* aNodes2 [3];
877     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
878       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
879       if ( aNodes2[ i ] == theNode2 )
880         iB2 = i; // node B in tr2
881       else if ( aNodes2[ i ] != theNode1 )
882         i2 = i;  // node 2
883     }
884
885     // nodes 1 and 2 should not be the same
886     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
887       return false;
888
889     // tr1: A->2
890     aNodes1[ iA1 ] = aNodes2[ i2 ];
891     // tr2: B->1
892     aNodes2[ iB2 ] = aNodes1[ i1 ];
893
894     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
895     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
896
897     return true;
898   }
899
900   // check case of quadratic faces
901   return InverseDiag(tr1,tr2);
902 }
903
904 //=======================================================================
905 //function : getQuadrangleNodes
906 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
907 //           fusion of triangles tr1 and tr2 having shared link on
908 //           theNode1 and theNode2
909 //=======================================================================
910
911 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
912                         const SMDS_MeshNode *    theNode1,
913                         const SMDS_MeshNode *    theNode2,
914                         const SMDS_MeshElement * tr1,
915                         const SMDS_MeshElement * tr2 )
916 {
917   if( tr1->NbNodes() != tr2->NbNodes() )
918     return false;
919   // find the 4-th node to insert into tr1
920   const SMDS_MeshNode* n4 = 0;
921   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
922   int i=0;
923   while ( !n4 && i<3 ) {
924     const SMDS_MeshNode * n = cast2Node( it->next() );
925     i++;
926     bool isDiag = ( n == theNode1 || n == theNode2 );
927     if ( !isDiag )
928       n4 = n;
929   }
930   // Make an array of nodes to be in a quadrangle
931   int iNode = 0, iFirstDiag = -1;
932   it = tr1->nodesIterator();
933   i=0;
934   while ( i<3 ) {
935     const SMDS_MeshNode * n = cast2Node( it->next() );
936     i++;
937     bool isDiag = ( n == theNode1 || n == theNode2 );
938     if ( isDiag ) {
939       if ( iFirstDiag < 0 )
940         iFirstDiag = iNode;
941       else if ( iNode - iFirstDiag == 1 )
942         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
943     }
944     else if ( n == n4 ) {
945       return false; // tr1 and tr2 should not have all the same nodes
946     }
947     theQuadNodes[ iNode++ ] = n;
948   }
949   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
950     theQuadNodes[ iNode ] = n4;
951
952   return true;
953 }
954
955 //=======================================================================
956 //function : DeleteDiag
957 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
958 //           with a quadrangle built on the same 4 nodes.
959 //           Return false if proper faces not found
960 //=======================================================================
961
962 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
963                                    const SMDS_MeshNode * theNode2)
964 {
965   myLastCreatedElems.Clear();
966   myLastCreatedNodes.Clear();
967
968   MESSAGE( "::DeleteDiag()" );
969
970   const SMDS_MeshElement *tr1, *tr2;
971   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
972     return false;
973
974   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
975   if (!F1) return false;
976   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
977   if (!F2) return false;
978   SMESHDS_Mesh * aMesh = GetMeshDS();
979
980   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
981       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
982
983     const SMDS_MeshNode* aNodes [ 4 ];
984     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
985       return false;
986
987     const SMDS_MeshElement* newElem = 0;
988     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
989     myLastCreatedElems.Append(newElem);
990     AddToSameGroups( newElem, tr1, aMesh );
991     int aShapeId = tr1->getshapeId();
992     if ( aShapeId )
993       {
994         aMesh->SetMeshElementOnShape( newElem, aShapeId );
995       }
996     aMesh->RemoveElement( tr1 );
997     aMesh->RemoveElement( tr2 );
998
999     return true;
1000   }
1001
1002   // check case of quadratic faces
1003   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1004     return false;
1005   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1006     return false;
1007
1008   //       5
1009   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1010   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1011   //    |   / |
1012   //  7 +  +  + 6
1013   //    | /9  |
1014   //    |/    |
1015   //  4 +--+--+ 3
1016   //       8
1017
1018   vector< const SMDS_MeshNode* > N1;
1019   vector< const SMDS_MeshNode* > N2;
1020   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1021     return false;
1022   // now we receive following N1 and N2 (using numeration as above image)
1023   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1024   // i.e. first nodes from both arrays determ new diagonal
1025
1026   const SMDS_MeshNode* aNodes[8];
1027   aNodes[0] = N1[0];
1028   aNodes[1] = N1[1];
1029   aNodes[2] = N2[0];
1030   aNodes[3] = N2[1];
1031   aNodes[4] = N1[3];
1032   aNodes[5] = N2[5];
1033   aNodes[6] = N2[3];
1034   aNodes[7] = N1[5];
1035
1036   const SMDS_MeshElement* newElem = 0;
1037   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1038                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1039   myLastCreatedElems.Append(newElem);
1040   AddToSameGroups( newElem, tr1, aMesh );
1041   int aShapeId = tr1->getshapeId();
1042   if ( aShapeId )
1043     {
1044       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045     }
1046   aMesh->RemoveElement( tr1 );
1047   aMesh->RemoveElement( tr2 );
1048
1049   // remove middle node (9)
1050   GetMeshDS()->RemoveNode( N1[4] );
1051
1052   return true;
1053 }
1054
1055 //=======================================================================
1056 //function : Reorient
1057 //purpose  : Reverse theElement orientation
1058 //=======================================================================
1059
1060 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1061 {
1062   MESSAGE("Reorient");
1063   myLastCreatedElems.Clear();
1064   myLastCreatedNodes.Clear();
1065
1066   if (!theElem)
1067     return false;
1068   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1069   if ( !it || !it->more() )
1070     return false;
1071
1072   const SMDSAbs_ElementType type = theElem->GetType();
1073   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1074     return false;
1075
1076   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1077   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1078   {
1079     const SMDS_VtkVolume* aPolyedre =
1080       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1081     if (!aPolyedre) {
1082       MESSAGE("Warning: bad volumic element");
1083       return false;
1084     }
1085     const int nbFaces = aPolyedre->NbFaces();
1086     vector<const SMDS_MeshNode *> poly_nodes;
1087     vector<int> quantities (nbFaces);
1088
1089     // reverse each face of the polyedre
1090     for (int iface = 1; iface <= nbFaces; iface++) {
1091       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1092       quantities[iface - 1] = nbFaceNodes;
1093
1094       for (inode = nbFaceNodes; inode >= 1; inode--) {
1095         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1096         poly_nodes.push_back(curNode);
1097       }
1098     }
1099     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100   }
1101   else // other elements
1102   {
1103     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1104     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1105     if ( interlace.empty() )
1106     {
1107       std::reverse( nodes.begin(), nodes.end() ); // polygon
1108     }
1109     else if ( interlace.size() > 1 )
1110     {
1111       SMDS_MeshCell::applyInterlace( interlace, nodes );
1112     }
1113     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1114   }
1115   return false;
1116 }
1117
1118 //================================================================================
1119 /*!
1120  * \brief Reorient faces.
1121  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1122  * \param theDirection - desired direction of normal of \a theFace
1123  * \param theFace - one of \a theFaces that sould be oriented according to
1124  *        \a theDirection and whose orientation defines orientation of other faces
1125  * \return number of reoriented faces.
1126  */
1127 //================================================================================
1128
1129 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1130                                   const gp_Dir&            theDirection,
1131                                   const SMDS_MeshElement * theFace)
1132 {
1133   int nbReori = 0;
1134   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1135
1136   if ( theFaces.empty() )
1137   {
1138     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1139     while ( fIt->more() )
1140       theFaces.insert( theFaces.end(), fIt->next() );
1141   }
1142
1143   // orient theFace according to theDirection
1144   gp_XYZ normal;
1145   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1146   if ( normal * theDirection.XYZ() < 0 )
1147     nbReori += Reorient( theFace );
1148
1149   // Orient other faces
1150
1151   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1152   TIDSortedElemSet avoidSet;
1153   set< SMESH_TLink > checkedLinks;
1154   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1155
1156   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1157     theFaces.erase( theFace );
1158   startFaces.insert( theFace );
1159
1160   int nodeInd1, nodeInd2;
1161   const SMDS_MeshElement*           otherFace;
1162   vector< const SMDS_MeshElement* > facesNearLink;
1163   vector< std::pair< int, int > >   nodeIndsOfFace;
1164
1165   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1166   while ( !startFaces.empty() )
1167   {
1168     startFace = startFaces.begin();
1169     theFace = *startFace;
1170     startFaces.erase( startFace );
1171     if ( !visitedFaces.insert( theFace ).second )
1172       continue;
1173
1174     avoidSet.clear();
1175     avoidSet.insert(theFace);
1176
1177     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1178
1179     const int nbNodes = theFace->NbCornerNodes();
1180     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1181     {
1182       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1183       linkIt_isNew = checkedLinks.insert( link );
1184       if ( !linkIt_isNew.second )
1185       {
1186         // link has already been checked and won't be encountered more
1187         // if the group (theFaces) is manifold
1188         //checkedLinks.erase( linkIt_isNew.first );
1189       }
1190       else
1191       {
1192         facesNearLink.clear();
1193         nodeIndsOfFace.clear();
1194         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1195                                                              theFaces, avoidSet,
1196                                                              &nodeInd1, &nodeInd2 )))
1197           if ( otherFace != theFace)
1198           {
1199             facesNearLink.push_back( otherFace );
1200             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1201             avoidSet.insert( otherFace );
1202           }
1203         if ( facesNearLink.size() > 1 )
1204         {
1205           // NON-MANIFOLD mesh shell !
1206           // select a face most co-directed with theFace,
1207           // other faces won't be visited this time
1208           gp_XYZ NF, NOF;
1209           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1210           double proj, maxProj = -1;
1211           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1212             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1213             if (( proj = Abs( NF * NOF )) > maxProj ) {
1214               maxProj = proj;
1215               otherFace = facesNearLink[i];
1216               nodeInd1  = nodeIndsOfFace[i].first;
1217               nodeInd2  = nodeIndsOfFace[i].second;
1218             }
1219           }
1220           // not to visit rejected faces
1221           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1222             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1223               visitedFaces.insert( facesNearLink[i] );
1224         }
1225         else if ( facesNearLink.size() == 1 )
1226         {
1227           otherFace = facesNearLink[0];
1228           nodeInd1  = nodeIndsOfFace.back().first;
1229           nodeInd2  = nodeIndsOfFace.back().second;
1230         }
1231         if ( otherFace && otherFace != theFace)
1232         {
1233           // link must be reverse in otherFace if orientation ot otherFace
1234           // is same as that of theFace
1235           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1236           {
1237             nbReori += Reorient( otherFace );
1238           }
1239           startFaces.insert( otherFace );
1240         }
1241       }
1242       std::swap( link.first, link.second ); // reverse the link
1243     }
1244   }
1245   return nbReori;
1246 }
1247
1248 //=======================================================================
1249 //function : getBadRate
1250 //purpose  :
1251 //=======================================================================
1252
1253 static double getBadRate (const SMDS_MeshElement*               theElem,
1254                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1255 {
1256   SMESH::Controls::TSequenceOfXYZ P;
1257   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1258     return 1e100;
1259   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1260   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1261 }
1262
1263 //=======================================================================
1264 //function : QuadToTri
1265 //purpose  : Cut quadrangles into triangles.
1266 //           theCrit is used to select a diagonal to cut
1267 //=======================================================================
1268
1269 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1270                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1271 {
1272   myLastCreatedElems.Clear();
1273   myLastCreatedNodes.Clear();
1274
1275   if ( !theCrit.get() )
1276     return false;
1277
1278   SMESHDS_Mesh * aMesh = GetMeshDS();
1279
1280   Handle(Geom_Surface) surface;
1281   SMESH_MesherHelper   helper( *GetMesh() );
1282
1283   TIDSortedElemSet::iterator itElem;
1284   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1285   {
1286     const SMDS_MeshElement* elem = *itElem;
1287     if ( !elem || elem->GetType() != SMDSAbs_Face )
1288       continue;
1289     if ( elem->NbCornerNodes() != 4 )
1290       continue;
1291
1292     // retrieve element nodes
1293     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1294
1295     // compare two sets of possible triangles
1296     double aBadRate1, aBadRate2; // to what extent a set is bad
1297     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1298     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1299     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1300
1301     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1302     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1303     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1304
1305     const int aShapeId = FindShape( elem );
1306     const SMDS_MeshElement* newElem1 = 0;
1307     const SMDS_MeshElement* newElem2 = 0;
1308
1309     if ( !elem->IsQuadratic() ) // split liner quadrangle
1310     {
1311       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1312       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1313       if ( aBadRate1 <= aBadRate2 ) {
1314         // tr1 + tr2 is better
1315         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1316         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1317       }
1318       else {
1319         // tr3 + tr4 is better
1320         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1321         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1322       }
1323     }
1324     else // split quadratic quadrangle
1325     {
1326       helper.SetIsQuadratic( true );
1327       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1328
1329       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1330       if ( aNodes.size() == 9 )
1331       {
1332         helper.SetIsBiQuadratic( true );
1333         if ( aBadRate1 <= aBadRate2 )
1334           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1335         else
1336           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1337       }
1338       // create a new element
1339       if ( aBadRate1 <= aBadRate2 ) {
1340         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1341         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1342       }
1343       else {
1344         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1345         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1346       }
1347     } // quadratic case
1348
1349     // care of a new element
1350
1351     myLastCreatedElems.Append(newElem1);
1352     myLastCreatedElems.Append(newElem2);
1353     AddToSameGroups( newElem1, elem, aMesh );
1354     AddToSameGroups( newElem2, elem, aMesh );
1355
1356     // put a new triangle on the same shape
1357     if ( aShapeId )
1358       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1359     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1360
1361     aMesh->RemoveElement( elem );
1362   }
1363   return true;
1364 }
1365
1366 //=======================================================================
1367 /*!
1368  * \brief Split each of given quadrangles into 4 triangles.
1369  * \param theElems - The faces to be splitted. If empty all faces are split.
1370  */
1371 //=======================================================================
1372
1373 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1374 {
1375   myLastCreatedElems.Clear();
1376   myLastCreatedNodes.Clear();
1377
1378   SMESH_MesherHelper helper( *GetMesh() );
1379   helper.SetElementsOnShape( true );
1380
1381   SMDS_ElemIteratorPtr faceIt;
1382   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1383   else                    faceIt = elemSetIterator( theElems );
1384
1385   bool   checkUV;
1386   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1387   gp_XYZ xyz[9];
1388   vector< const SMDS_MeshNode* > nodes;
1389   SMESHDS_SubMesh*               subMeshDS;
1390   TopoDS_Face                    F;
1391   Handle(Geom_Surface)           surface;
1392   TopLoc_Location                loc;
1393
1394   while ( faceIt->more() )
1395   {
1396     const SMDS_MeshElement* quad = faceIt->next();
1397     if ( !quad || quad->NbCornerNodes() != 4 )
1398       continue;
1399
1400     // get a surface the quad is on
1401
1402     if ( quad->getshapeId() < 1 )
1403     {
1404       F.Nullify();
1405       helper.SetSubShape( 0 );
1406       subMeshDS = 0;
1407     }
1408     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1409     {
1410       helper.SetSubShape( quad->getshapeId() );
1411       if ( !helper.GetSubShape().IsNull() &&
1412            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1413       {
1414         F = TopoDS::Face( helper.GetSubShape() );
1415         surface = BRep_Tool::Surface( F, loc );
1416         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1417       }
1418       else
1419       {
1420         helper.SetSubShape( 0 );
1421         subMeshDS = 0;
1422       }
1423     }
1424
1425     // create a central node
1426
1427     const SMDS_MeshNode* nCentral;
1428     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1429
1430     if ( nodes.size() == 9 )
1431     {
1432       nCentral = nodes.back();
1433     }
1434     else
1435     {
1436       size_t iN = 0;
1437       if ( F.IsNull() )
1438       {
1439         for ( ; iN < nodes.size(); ++iN )
1440           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1441
1442         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1443           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1444
1445         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1446                                    xyz[0], xyz[1], xyz[2], xyz[3],
1447                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1448       }
1449       else
1450       {
1451         for ( ; iN < nodes.size(); ++iN )
1452           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1453
1454         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1455           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1456
1457         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1458                                   uv[0], uv[1], uv[2], uv[3],
1459                                   uv[4], uv[5], uv[6], uv[7] );
1460
1461         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1462         xyz[ 8 ] = p.XYZ();
1463       }
1464
1465       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1466                                  uv[8].X(), uv[8].Y() );
1467       myLastCreatedNodes.Append( nCentral );
1468     }
1469
1470     // create 4 triangles
1471
1472     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1473     
1474     helper.SetIsQuadratic  ( nodes.size() > 4 );
1475     helper.SetIsBiQuadratic( nodes.size() == 9 );
1476     if ( helper.GetIsQuadratic() )
1477       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1478
1479     for ( int i = 0; i < 4; ++i )
1480     {
1481       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1482                                                nodes[(i+1)%4],
1483                                                nCentral );
1484       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1485       myLastCreatedElems.Append( tria );
1486     }
1487   }
1488 }
1489
1490 //=======================================================================
1491 //function : BestSplit
1492 //purpose  : Find better diagonal for cutting.
1493 //=======================================================================
1494
1495 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1496                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1497 {
1498   myLastCreatedElems.Clear();
1499   myLastCreatedNodes.Clear();
1500
1501   if (!theCrit.get())
1502     return -1;
1503
1504   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1505     return -1;
1506
1507   if( theQuad->NbNodes()==4 ||
1508       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1509
1510     // retrieve element nodes
1511     const SMDS_MeshNode* aNodes [4];
1512     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1513     int i = 0;
1514     //while (itN->more())
1515     while (i<4) {
1516       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1517     }
1518     // compare two sets of possible triangles
1519     double aBadRate1, aBadRate2; // to what extent a set is bad
1520     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1521     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1522     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1523
1524     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1525     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1526     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1527     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1528     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1529     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1530       return 1; // diagonal 1-3
1531
1532     return 2; // diagonal 2-4
1533   }
1534   return -1;
1535 }
1536
1537 namespace
1538 {
1539   // Methods of splitting volumes into tetra
1540
1541   const int theHexTo5_1[5*4+1] =
1542     {
1543       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1544     };
1545   const int theHexTo5_2[5*4+1] =
1546     {
1547       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1548     };
1549   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1550
1551   const int theHexTo6_1[6*4+1] =
1552     {
1553       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1554     };
1555   const int theHexTo6_2[6*4+1] =
1556     {
1557       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1558     };
1559   const int theHexTo6_3[6*4+1] =
1560     {
1561       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1562     };
1563   const int theHexTo6_4[6*4+1] =
1564     {
1565       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1566     };
1567   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1568
1569   const int thePyraTo2_1[2*4+1] =
1570     {
1571       0, 1, 2, 4,    0, 2, 3, 4,   -1
1572     };
1573   const int thePyraTo2_2[2*4+1] =
1574     {
1575       1, 2, 3, 4,    1, 3, 0, 4,   -1
1576     };
1577   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1578
1579   const int thePentaTo3_1[3*4+1] =
1580     {
1581       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1582     };
1583   const int thePentaTo3_2[3*4+1] =
1584     {
1585       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1586     };
1587   const int thePentaTo3_3[3*4+1] =
1588     {
1589       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1590     };
1591   const int thePentaTo3_4[3*4+1] =
1592     {
1593       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1594     };
1595   const int thePentaTo3_5[3*4+1] =
1596     {
1597       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1598     };
1599   const int thePentaTo3_6[3*4+1] =
1600     {
1601       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1602     };
1603   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1604                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1605
1606   // Methods of splitting hexahedron into prisms
1607
1608   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1609     {
1610       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1611     };
1612   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1613     {
1614       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1615     };
1616   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1617     {
1618       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1619     };
1620
1621   const int theHexTo2Prisms_BT_1[6*2+1] =
1622     {
1623       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1624     };
1625   const int theHexTo2Prisms_BT_2[6*2+1] =
1626     {
1627       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1628     };
1629   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1630
1631   const int theHexTo2Prisms_LR_1[6*2+1] =
1632     {
1633       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1634     };
1635   const int theHexTo2Prisms_LR_2[6*2+1] =
1636     {
1637       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1638     };
1639   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1640
1641   const int theHexTo2Prisms_FB_1[6*2+1] =
1642     {
1643       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1644     };
1645   const int theHexTo2Prisms_FB_2[6*2+1] =
1646     {
1647       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1648     };
1649   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1650
1651
1652   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1653   {
1654     int _n1, _n2, _n3;
1655     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1656     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1657     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1658                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1659   };
1660   struct TSplitMethod
1661   {
1662     int        _nbSplits;
1663     int        _nbCorners;
1664     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1665     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1666     bool       _ownConn;      //!< to delete _connectivity in destructor
1667     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1668
1669     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1670       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1671     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1672     bool hasFacet( const TTriangleFacet& facet ) const
1673     {
1674       if ( _nbCorners == 4 )
1675       {
1676         const int* tetConn = _connectivity;
1677         for ( ; tetConn[0] >= 0; tetConn += 4 )
1678           if (( facet.contains( tetConn[0] ) +
1679                 facet.contains( tetConn[1] ) +
1680                 facet.contains( tetConn[2] ) +
1681                 facet.contains( tetConn[3] )) == 3 )
1682             return true;
1683       }
1684       else // prism, _nbCorners == 6
1685       {
1686         const int* prismConn = _connectivity;
1687         for ( ; prismConn[0] >= 0; prismConn += 6 )
1688         {
1689           if (( facet.contains( prismConn[0] ) &&
1690                 facet.contains( prismConn[1] ) &&
1691                 facet.contains( prismConn[2] ))
1692               ||
1693               ( facet.contains( prismConn[3] ) &&
1694                 facet.contains( prismConn[4] ) &&
1695                 facet.contains( prismConn[5] )))
1696             return true;
1697         }
1698       }
1699       return false;
1700     }
1701   };
1702
1703   //=======================================================================
1704   /*!
1705    * \brief return TSplitMethod for the given element to split into tetrahedra
1706    */
1707   //=======================================================================
1708
1709   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1710   {
1711     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1712
1713     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1714     // an edge and a face barycenter; tertaherdons are based on triangles and
1715     // a volume barycenter
1716     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1717
1718     // Find out how adjacent volumes are split
1719
1720     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1721     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1722     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1723     {
1724       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1725       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1726       if ( nbNodes < 4 ) continue;
1727
1728       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1729       const int* nInd = vol.GetFaceNodesIndices( iF );
1730       if ( nbNodes == 4 )
1731       {
1732         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1733         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1734         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1735         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1736       }
1737       else
1738       {
1739         int iCom = 0; // common node of triangle faces to split into
1740         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1741         {
1742           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1743                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1744                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1745           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1746                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1747                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1748           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1749           {
1750             triaSplits.push_back( t012 );
1751             triaSplits.push_back( t023 );
1752             break;
1753           }
1754         }
1755       }
1756       if ( !triaSplits.empty() )
1757         hasAdjacentSplits = true;
1758     }
1759
1760     // Among variants of split method select one compliant with adjacent volumes
1761
1762     TSplitMethod method;
1763     if ( !vol.Element()->IsPoly() && !is24TetMode )
1764     {
1765       int nbVariants = 2, nbTet = 0;
1766       const int** connVariants = 0;
1767       switch ( vol.Element()->GetEntityType() )
1768       {
1769       case SMDSEntity_Hexa:
1770       case SMDSEntity_Quad_Hexa:
1771       case SMDSEntity_TriQuad_Hexa:
1772         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1773           connVariants = theHexTo5, nbTet = 5;
1774         else
1775           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1776         break;
1777       case SMDSEntity_Pyramid:
1778       case SMDSEntity_Quad_Pyramid:
1779         connVariants = thePyraTo2;  nbTet = 2;
1780         break;
1781       case SMDSEntity_Penta:
1782       case SMDSEntity_Quad_Penta:
1783         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1784         break;
1785       default:
1786         nbVariants = 0;
1787       }
1788       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1789       {
1790         // check method compliancy with adjacent tetras,
1791         // all found splits must be among facets of tetras described by this method
1792         method = TSplitMethod( nbTet, connVariants[variant] );
1793         if ( hasAdjacentSplits && method._nbSplits > 0 )
1794         {
1795           bool facetCreated = true;
1796           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1797           {
1798             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1799             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1800               facetCreated = method.hasFacet( *facet );
1801           }
1802           if ( !facetCreated )
1803             method = TSplitMethod(0); // incompatible method
1804         }
1805       }
1806     }
1807     if ( method._nbSplits < 1 )
1808     {
1809       // No standard method is applicable, use a generic solution:
1810       // each facet of a volume is split into triangles and
1811       // each of triangles and a volume barycenter form a tetrahedron.
1812
1813       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1814
1815       int* connectivity = new int[ maxTetConnSize + 1 ];
1816       method._connectivity = connectivity;
1817       method._ownConn = true;
1818       method._baryNode = !isHex27; // to create central node or not
1819
1820       int connSize = 0;
1821       int baryCenInd = vol.NbNodes() - int( isHex27 );
1822       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1823       {
1824         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1825         const int*   nInd = vol.GetFaceNodesIndices( iF );
1826         // find common node of triangle facets of tetra to create
1827         int iCommon = 0; // index in linear numeration
1828         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1829         if ( !triaSplits.empty() )
1830         {
1831           // by found facets
1832           const TTriangleFacet* facet = &triaSplits.front();
1833           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1834             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1835                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1836               break;
1837         }
1838         else if ( nbNodes > 3 && !is24TetMode )
1839         {
1840           // find the best method of splitting into triangles by aspect ratio
1841           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1842           map< double, int > badness2iCommon;
1843           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1844           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1845           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1846           {
1847             double badness = 0;
1848             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1849             {
1850               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1851                                       nodes[ iQ*((iLast-1)%nbNodes)],
1852                                       nodes[ iQ*((iLast  )%nbNodes)]);
1853               badness += getBadRate( &tria, aspectRatio );
1854             }
1855             badness2iCommon.insert( make_pair( badness, iCommon ));
1856           }
1857           // use iCommon with lowest badness
1858           iCommon = badness2iCommon.begin()->second;
1859         }
1860         if ( iCommon >= nbNodes )
1861           iCommon = 0; // something wrong
1862
1863         // fill connectivity of tetrahedra based on a current face
1864         int nbTet = nbNodes - 2;
1865         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1866         {
1867           int faceBaryCenInd;
1868           if ( isHex27 )
1869           {
1870             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1871             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1872           }
1873           else
1874           {
1875             method._faceBaryNode[ iF ] = 0;
1876             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1877           }
1878           nbTet = nbNodes;
1879           for ( int i = 0; i < nbTet; ++i )
1880           {
1881             int i1 = i, i2 = (i+1) % nbNodes;
1882             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1883             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1884             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1885             connectivity[ connSize++ ] = faceBaryCenInd;
1886             connectivity[ connSize++ ] = baryCenInd;
1887           }
1888         }
1889         else
1890         {
1891           for ( int i = 0; i < nbTet; ++i )
1892           {
1893             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1894             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1895             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1896             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1897             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1898             connectivity[ connSize++ ] = baryCenInd;
1899           }
1900         }
1901         method._nbSplits += nbTet;
1902
1903       } // loop on volume faces
1904
1905       connectivity[ connSize++ ] = -1;
1906
1907     } // end of generic solution
1908
1909     return method;
1910   }
1911   //=======================================================================
1912   /*!
1913    * \brief return TSplitMethod to split haxhedron into prisms
1914    */
1915   //=======================================================================
1916
1917   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
1918                                     const int        methodFlags,
1919                                     const int        facetToSplit)
1920   {
1921     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
1922     // B, T, L, B, R, F
1923     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
1924
1925     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
1926     {
1927       static TSplitMethod to4methods[4]; // order BT, LR, FB
1928       if ( to4methods[iF]._nbSplits == 0 )
1929       {
1930         switch ( iF ) {
1931         case 0:
1932           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
1933           to4methods[iF]._faceBaryNode[ 0 ] = 0;
1934           to4methods[iF]._faceBaryNode[ 1 ] = 0;
1935           break;
1936         case 1:
1937           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
1938           to4methods[iF]._faceBaryNode[ 2 ] = 0;
1939           to4methods[iF]._faceBaryNode[ 4 ] = 0;
1940           break;
1941         case 2:
1942           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
1943           to4methods[iF]._faceBaryNode[ 3 ] = 0;
1944           to4methods[iF]._faceBaryNode[ 5 ] = 0;
1945           break;
1946         default: return to4methods[3];
1947         }
1948         to4methods[iF]._nbSplits  = 4;
1949         to4methods[iF]._nbCorners = 6;
1950       }
1951       return to4methods[iF];
1952     }
1953     // else if ( methodFlags == HEXA_TO_2_PRISMS )
1954
1955     TSplitMethod method;
1956
1957     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1958
1959     const int nbVariants = 2, nbSplits = 2;
1960     const int** connVariants = 0;
1961     switch ( iF ) {
1962     case 0: connVariants = theHexTo2Prisms_BT; break;
1963     case 1: connVariants = theHexTo2Prisms_LR; break;
1964     case 2: connVariants = theHexTo2Prisms_FB; break;
1965     default: return method;
1966     }
1967
1968     // look for prisms adjacent via facetToSplit and an opposite one
1969     for ( int is2nd = 0; is2nd < 2; ++is2nd )
1970     {
1971       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
1972       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
1973       if ( nbNodes != 4 ) return method;
1974
1975       const int* nInd = vol.GetFaceNodesIndices( iFacet );
1976       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1977       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1978       TTriangleFacet* t;
1979       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1980         t = &t012;
1981       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1982         t = &t123;
1983       else
1984         continue;
1985
1986       // there are adjacent prism
1987       for ( int variant = 0; variant < nbVariants; ++variant )
1988       {
1989         // check method compliancy with adjacent prisms,
1990         // the found prism facets must be among facets of prisms described by current method
1991         method._nbSplits     = nbSplits;
1992         method._nbCorners    = 6;
1993         method._connectivity = connVariants[ variant ];
1994         if ( method.hasFacet( *t ))
1995           return method;
1996       }
1997     }
1998
1999     // No adjacent prisms. Select a variant with a best aspect ratio.
2000
2001     double badness[2] = { 0, 0 };
2002     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2003     const SMDS_MeshNode** nodes = vol.GetNodes();
2004     for ( int variant = 0; variant < nbVariants; ++variant )
2005       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2006       {
2007         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2008         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2009
2010         method._connectivity = connVariants[ variant ];
2011         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2012         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2013         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2014
2015         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2016                                 nodes[ t->_n2 ],
2017                                 nodes[ t->_n3 ] );
2018         badness[ variant ] += getBadRate( &tria, aspectRatio );
2019       }
2020     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2021
2022     method._nbSplits     = nbSplits;
2023     method._nbCorners    = 6;
2024     method._connectivity = connVariants[ iBetter ];
2025
2026     return method;
2027   }
2028
2029   //================================================================================
2030   /*!
2031    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2032    */
2033   //================================================================================
2034
2035   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2036                                        const SMDSAbs_GeometryType geom ) const
2037   {
2038     // find the tetrahedron including the three nodes of facet
2039     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2040     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2041     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2042     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2043     while ( volIt1->more() )
2044     {
2045       const SMDS_MeshElement* v = volIt1->next();
2046       if ( v->GetGeomType() != geom )
2047         continue;
2048       const int lastCornerInd = v->NbCornerNodes() - 1;
2049       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2050         continue; // medium node not allowed
2051       const int ind2 = v->GetNodeIndex( n2 );
2052       if ( ind2 < 0 || lastCornerInd < ind2 )
2053         continue;
2054       const int ind3 = v->GetNodeIndex( n3 );
2055       if ( ind3 < 0 || lastCornerInd < ind3 )
2056         continue;
2057       return true;
2058     }
2059     return false;
2060   }
2061
2062   //=======================================================================
2063   /*!
2064    * \brief A key of a face of volume
2065    */
2066   //=======================================================================
2067
2068   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2069   {
2070     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2071     {
2072       TIDSortedNodeSet sortedNodes;
2073       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2074       int nbNodes = vol.NbFaceNodes( iF );
2075       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2076       for ( int i = 0; i < nbNodes; i += iQ )
2077         sortedNodes.insert( fNodes[i] );
2078       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2079       first.first   = (*(n++))->GetID();
2080       first.second  = (*(n++))->GetID();
2081       second.first  = (*(n++))->GetID();
2082       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2083     }
2084   };
2085 } // namespace
2086
2087 //=======================================================================
2088 //function : SplitVolumes
2089 //purpose  : Split volume elements into tetrahedra or prisms.
2090 //           If facet ID < 0, element is split into tetrahedra,
2091 //           else a hexahedron is split into prisms so that the given facet is
2092 //           split into triangles
2093 //=======================================================================
2094
2095 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2096                                      const int            theMethodFlags)
2097 {
2098   // std-like iterator on coordinates of nodes of mesh element
2099   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2100   NXyzIterator xyzEnd;
2101
2102   SMDS_VolumeTool    volTool;
2103   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2104   fHelper.ToFixNodeParameters( true );
2105
2106   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2107   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2108
2109   SMESH_SequenceOfElemPtr newNodes, newElems;
2110
2111   // map face of volume to it's baricenrtic node
2112   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2113   double bc[3];
2114
2115   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2116   for ( ; elem2facet != theElems.end(); ++elem2facet )
2117   {
2118     const SMDS_MeshElement* elem = elem2facet->first;
2119     const int       facetToSplit = elem2facet->second;
2120     if ( elem->GetType() != SMDSAbs_Volume )
2121       continue;
2122     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2123     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2124       continue;
2125
2126     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2127
2128     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2129                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2130                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2131     if ( splitMethod._nbSplits < 1 ) continue;
2132
2133     // find submesh to add new tetras to
2134     if ( !subMesh || !subMesh->Contains( elem ))
2135     {
2136       int shapeID = FindShape( elem );
2137       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2138       subMesh = GetMeshDS()->MeshElements( shapeID );
2139     }
2140     int iQ;
2141     if ( elem->IsQuadratic() )
2142     {
2143       iQ = 2;
2144       // add quadratic links to the helper
2145       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2146       {
2147         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2148         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2149         for ( int iN = 0; iN < nbN; iN += iQ )
2150           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2151       }
2152       helper.SetIsQuadratic( true );
2153     }
2154     else
2155     {
2156       iQ = 1;
2157       helper.SetIsQuadratic( false );
2158     }
2159     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2160                                         volTool.GetNodes() + elem->NbNodes() );
2161     helper.SetElementsOnShape( true );
2162     if ( splitMethod._baryNode )
2163     {
2164       // make a node at barycenter
2165       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2166       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2167       nodes.push_back( gcNode );
2168       newNodes.Append( gcNode );
2169     }
2170     if ( !splitMethod._faceBaryNode.empty() )
2171     {
2172       // make or find baricentric nodes of faces
2173       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2174       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2175       {
2176         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2177           volFace2BaryNode.insert
2178           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2179         if ( !f_n->second )
2180         {
2181           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2182           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2183         }
2184         nodes.push_back( iF_n->second = f_n->second );
2185       }
2186     }
2187
2188     // make new volumes
2189     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2190     const int* volConn = splitMethod._connectivity;
2191     if ( splitMethod._nbCorners == 4 ) // tetra
2192       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2193         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2194                                                             nodes[ volConn[1] ],
2195                                                             nodes[ volConn[2] ],
2196                                                             nodes[ volConn[3] ]));
2197     else // prisms
2198       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2199         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2200                                                             nodes[ volConn[1] ],
2201                                                             nodes[ volConn[2] ],
2202                                                             nodes[ volConn[3] ],
2203                                                             nodes[ volConn[4] ],
2204                                                             nodes[ volConn[5] ]));
2205
2206     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2207
2208     // Split faces on sides of the split volume
2209
2210     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2211     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2212     {
2213       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2214       if ( nbNodes < 4 ) continue;
2215
2216       // find an existing face
2217       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2218                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2219       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2220                                                                        /*noMedium=*/false))
2221       {
2222         // make triangles
2223         helper.SetElementsOnShape( false );
2224         vector< const SMDS_MeshElement* > triangles;
2225
2226         // find submesh to add new triangles in
2227         if ( !fSubMesh || !fSubMesh->Contains( face ))
2228         {
2229           int shapeID = FindShape( face );
2230           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2231         }
2232         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2233         if ( iF_n != splitMethod._faceBaryNode.end() )
2234         {
2235           const SMDS_MeshNode *baryNode = iF_n->second;
2236           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2237           {
2238             const SMDS_MeshNode* n1 = fNodes[iN];
2239             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2240             const SMDS_MeshNode *n3 = baryNode;
2241             if ( !volTool.IsFaceExternal( iF ))
2242               swap( n2, n3 );
2243             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2244           }
2245           if ( fSubMesh ) // update position of the bary node on geometry
2246           {
2247             if ( subMesh )
2248               subMesh->RemoveNode( baryNode, false );
2249             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2250             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2251             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2252             {
2253               fHelper.SetSubShape( s );
2254               gp_XY uv( 1e100, 1e100 );
2255               double distXYZ[4];
2256               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2257                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2258                    uv.X() < 1e100 )
2259               {
2260                 // node is too far from the surface
2261                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2262                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2263                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2264               }
2265             }
2266           }
2267         }
2268         else
2269         {
2270           // among possible triangles create ones discribed by split method
2271           const int* nInd = volTool.GetFaceNodesIndices( iF );
2272           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2273           int iCom = 0; // common node of triangle faces to split into
2274           list< TTriangleFacet > facets;
2275           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2276           {
2277             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2278                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2279                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2280             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2281                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2282                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2283             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2284             {
2285               facets.push_back( t012 );
2286               facets.push_back( t023 );
2287               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2288                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2289                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2290                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2291               break;
2292             }
2293           }
2294           list< TTriangleFacet >::iterator facet = facets.begin();
2295           if ( facet == facets.end() )
2296             break;
2297           for ( ; facet != facets.end(); ++facet )
2298           {
2299             if ( !volTool.IsFaceExternal( iF ))
2300               swap( facet->_n2, facet->_n3 );
2301             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2302                                                  volNodes[ facet->_n2 ],
2303                                                  volNodes[ facet->_n3 ]));
2304           }
2305         }
2306         for ( int i = 0; i < triangles.size(); ++i )
2307         {
2308           if ( !triangles[i] ) continue;
2309           if ( fSubMesh )
2310             fSubMesh->AddElement( triangles[i]);
2311           newElems.Append( triangles[i] );
2312         }
2313         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2314         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2315
2316       } // while a face based on facet nodes exists
2317     } // loop on volume faces to split them into triangles
2318
2319     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2320
2321     if ( geomType == SMDSEntity_TriQuad_Hexa )
2322     {
2323       // remove medium nodes that could become free
2324       for ( int i = 20; i < volTool.NbNodes(); ++i )
2325         if ( volNodes[i]->NbInverseElements() == 0 )
2326           GetMeshDS()->RemoveNode( volNodes[i] );
2327     }
2328   } // loop on volumes to split
2329   
2330   myLastCreatedNodes = newNodes;
2331   myLastCreatedElems = newElems;
2332 }
2333
2334 //=======================================================================
2335 //function : GetHexaFacetsToSplit
2336 //purpose  : For hexahedra that will be split into prisms, finds facets to
2337 //           split into triangles. Only hexahedra adjacent to the one closest
2338 //           to theFacetNormal.Location() are returned.
2339 //param [in,out] theHexas - the hexahedra
2340 //param [in]     theFacetNormal - facet normal
2341 //param [out]    theFacets - the hexahedra and found facet IDs
2342 //=======================================================================
2343
2344 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2345                                              const gp_Ax1&     theFacetNormal,
2346                                              TFacetOfElem &    theFacets)
2347 {
2348   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2349
2350   // Find a hexa closest to the location of theFacetNormal
2351
2352   const SMDS_MeshElement* startHex;
2353   {
2354     // get SMDS_ElemIteratorPtr on theHexas
2355     typedef const SMDS_MeshElement*                                      TValue;
2356     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2357     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2358     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2359     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2360     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2361       ( new TElemSetIter( theHexas.begin(),
2362                           theHexas.end(),
2363                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2364
2365     SMESH_ElementSearcher* searcher =
2366       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2367
2368     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2369
2370     delete searcher;
2371
2372     if ( !startHex )
2373       throw SALOME_Exception( THIS_METHOD "startHex not found");
2374   }
2375
2376   // Select a facet of startHex by theFacetNormal
2377
2378   SMDS_VolumeTool vTool( startHex );
2379   double norm[3], dot, maxDot = 0;
2380   int facetID = -1;
2381   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2382     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2383     {
2384       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2385       if ( dot > maxDot )
2386       {
2387         facetID = iF;
2388         maxDot = dot;
2389       }
2390     }
2391   if ( facetID < 0 )
2392     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2393
2394   // Fill theFacets starting from facetID of startHex
2395
2396   // facets used for seach of volumes adjacent to already treated ones
2397   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2398   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2399   TFacetMap facetsToCheck;
2400
2401   set<const SMDS_MeshNode*> facetNodes;
2402   const SMDS_MeshElement*   curHex;
2403
2404   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2405
2406   while ( startHex )
2407   {
2408     // move in two directions from startHex via facetID
2409     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2410     {
2411       curHex       = startHex;
2412       int curFacet = facetID;
2413       if ( is2nd ) // do not treat startHex twice
2414       {
2415         vTool.Set( curHex );
2416         if ( vTool.IsFreeFace( curFacet, &curHex ))
2417         {
2418           curHex = 0;
2419         }
2420         else
2421         {
2422           vTool.GetFaceNodes( curFacet, facetNodes );
2423           vTool.Set( curHex );
2424           curFacet = vTool.GetFaceIndex( facetNodes );
2425         }
2426       }
2427       while ( curHex )
2428       {
2429         // store a facet to split
2430         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2431         {
2432           theFacets.insert( make_pair( curHex, -1 ));
2433           break;
2434         }
2435         if ( !allHex && !theHexas.count( curHex ))
2436           break;
2437
2438         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2439           theFacets.insert( make_pair( curHex, curFacet ));
2440         if ( !facetIt2isNew.second )
2441           break;
2442
2443         // remember not-to-split facets in facetsToCheck
2444         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2445         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2446         {
2447           if ( iF == curFacet && iF == oppFacet )
2448             continue;
2449           TVolumeFaceKey facetKey ( vTool, iF );
2450           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2451           pair< TFacetMap::iterator, bool > it2isnew =
2452             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2453           if ( !it2isnew.second )
2454             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2455         }
2456         // pass to a volume adjacent via oppFacet
2457         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2458         {
2459           curHex = 0;
2460         }
2461         else
2462         {
2463           // get a new curFacet
2464           vTool.GetFaceNodes( oppFacet, facetNodes );
2465           vTool.Set( curHex );
2466           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2467         }
2468       }
2469     } // move in two directions from startHex via facetID
2470
2471     // Find a new startHex by facetsToCheck
2472
2473     startHex = 0;
2474     facetID  = -1;
2475     TFacetMap::iterator fIt = facetsToCheck.begin();
2476     while ( !startHex && fIt != facetsToCheck.end() )
2477     {
2478       const TElemFacets&  elemFacets = fIt->second;
2479       const SMDS_MeshElement*    hex = elemFacets.first->first;
2480       int                 splitFacet = elemFacets.first->second;
2481       int               lateralFacet = elemFacets.second;
2482       facetsToCheck.erase( fIt );
2483       fIt = facetsToCheck.begin();
2484
2485       vTool.Set( hex );
2486       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2487            curHex->GetGeomType() != SMDSGeom_HEXA )
2488         continue;
2489       if ( !allHex && !theHexas.count( curHex ))
2490         continue;
2491
2492       startHex = curHex;
2493
2494       // find a facet of startHex to split 
2495
2496       set<const SMDS_MeshNode*> lateralNodes;
2497       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2498       vTool.GetFaceNodes( splitFacet,   facetNodes );
2499       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2500       vTool.Set( startHex );
2501       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2502
2503       // look for a facet of startHex having common nodes with facetNodes
2504       // but not lateralFacet
2505       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2506       {
2507         if ( iF == lateralFacet )
2508           continue;
2509         int nbCommonNodes = 0;
2510         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2511         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2512           nbCommonNodes += facetNodes.count( nn[ iN ]);
2513
2514         if ( nbCommonNodes >= 2 )
2515         {
2516           facetID = iF;
2517           break;
2518         }
2519       }
2520       if ( facetID < 0 )
2521         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2522     }
2523   } //   while ( startHex )
2524 }
2525
2526 //=======================================================================
2527 //function : AddToSameGroups
2528 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2529 //=======================================================================
2530
2531 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2532                                         const SMDS_MeshElement* elemInGroups,
2533                                         SMESHDS_Mesh *          aMesh)
2534 {
2535   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2536   if (!groups.empty()) {
2537     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2538     for ( ; grIt != groups.end(); grIt++ ) {
2539       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2540       if ( group && group->Contains( elemInGroups ))
2541         group->SMDSGroup().Add( elemToAdd );
2542     }
2543   }
2544 }
2545
2546
2547 //=======================================================================
2548 //function : RemoveElemFromGroups
2549 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2550 //=======================================================================
2551 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2552                                              SMESHDS_Mesh *          aMesh)
2553 {
2554   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2555   if (!groups.empty())
2556   {
2557     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2558     for (; GrIt != groups.end(); GrIt++)
2559     {
2560       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2561       if (!grp || grp->IsEmpty()) continue;
2562       grp->SMDSGroup().Remove(removeelem);
2563     }
2564   }
2565 }
2566
2567 //================================================================================
2568 /*!
2569  * \brief Replace elemToRm by elemToAdd in the all groups
2570  */
2571 //================================================================================
2572
2573 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2574                                             const SMDS_MeshElement* elemToAdd,
2575                                             SMESHDS_Mesh *          aMesh)
2576 {
2577   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2578   if (!groups.empty()) {
2579     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2580     for ( ; grIt != groups.end(); grIt++ ) {
2581       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2582       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2583         group->SMDSGroup().Add( elemToAdd );
2584     }
2585   }
2586 }
2587
2588 //================================================================================
2589 /*!
2590  * \brief Replace elemToRm by elemToAdd in the all groups
2591  */
2592 //================================================================================
2593
2594 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2595                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2596                                             SMESHDS_Mesh *                         aMesh)
2597 {
2598   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2599   if (!groups.empty())
2600   {
2601     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2602     for ( ; grIt != groups.end(); grIt++ ) {
2603       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2604       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2605         for ( int i = 0; i < elemToAdd.size(); ++i )
2606           group->SMDSGroup().Add( elemToAdd[ i ] );
2607     }
2608   }
2609 }
2610
2611 //=======================================================================
2612 //function : QuadToTri
2613 //purpose  : Cut quadrangles into triangles.
2614 //           theCrit is used to select a diagonal to cut
2615 //=======================================================================
2616
2617 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2618                                   const bool         the13Diag)
2619 {
2620   myLastCreatedElems.Clear();
2621   myLastCreatedNodes.Clear();
2622
2623   MESSAGE( "::QuadToTri()" );
2624
2625   SMESHDS_Mesh * aMesh = GetMeshDS();
2626
2627   Handle(Geom_Surface) surface;
2628   SMESH_MesherHelper   helper( *GetMesh() );
2629
2630   TIDSortedElemSet::iterator itElem;
2631   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2632     const SMDS_MeshElement* elem = *itElem;
2633     if ( !elem || elem->GetType() != SMDSAbs_Face )
2634       continue;
2635     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2636     if(!isquad) continue;
2637
2638     if(elem->NbNodes()==4) {
2639       // retrieve element nodes
2640       const SMDS_MeshNode* aNodes [4];
2641       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2642       int i = 0;
2643       while ( itN->more() )
2644         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2645
2646       int aShapeId = FindShape( elem );
2647       const SMDS_MeshElement* newElem1 = 0;
2648       const SMDS_MeshElement* newElem2 = 0;
2649       if ( the13Diag ) {
2650         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2651         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2652       }
2653       else {
2654         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2655         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2656       }
2657       myLastCreatedElems.Append(newElem1);
2658       myLastCreatedElems.Append(newElem2);
2659       // put a new triangle on the same shape and add to the same groups
2660       if ( aShapeId )
2661         {
2662           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2663           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2664         }
2665       AddToSameGroups( newElem1, elem, aMesh );
2666       AddToSameGroups( newElem2, elem, aMesh );
2667       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2668       aMesh->RemoveElement( elem );
2669     }
2670
2671     // Quadratic quadrangle
2672
2673     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2674
2675       // get surface elem is on
2676       int aShapeId = FindShape( elem );
2677       if ( aShapeId != helper.GetSubShapeID() ) {
2678         surface.Nullify();
2679         TopoDS_Shape shape;
2680         if ( aShapeId > 0 )
2681           shape = aMesh->IndexToShape( aShapeId );
2682         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2683           TopoDS_Face face = TopoDS::Face( shape );
2684           surface = BRep_Tool::Surface( face );
2685           if ( !surface.IsNull() )
2686             helper.SetSubShape( shape );
2687         }
2688       }
2689
2690       const SMDS_MeshNode* aNodes [8];
2691       const SMDS_MeshNode* inFaceNode = 0;
2692       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2693       int i = 0;
2694       while ( itN->more() ) {
2695         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2696         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2697              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2698         {
2699           inFaceNode = aNodes[ i-1 ];
2700         }
2701       }
2702
2703       // find middle point for (0,1,2,3)
2704       // and create a node in this point;
2705       gp_XYZ p( 0,0,0 );
2706       if ( surface.IsNull() ) {
2707         for(i=0; i<4; i++)
2708           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2709         p /= 4;
2710       }
2711       else {
2712         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2713         gp_XY uv( 0,0 );
2714         for(i=0; i<4; i++)
2715           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2716         uv /= 4.;
2717         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2718       }
2719       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2720       myLastCreatedNodes.Append(newN);
2721
2722       // create a new element
2723       const SMDS_MeshElement* newElem1 = 0;
2724       const SMDS_MeshElement* newElem2 = 0;
2725       if ( the13Diag ) {
2726         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2727                                   aNodes[6], aNodes[7], newN );
2728         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2729                                   newN,      aNodes[4], aNodes[5] );
2730       }
2731       else {
2732         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2733                                   aNodes[7], aNodes[4], newN );
2734         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2735                                   newN,      aNodes[5], aNodes[6] );
2736       }
2737       myLastCreatedElems.Append(newElem1);
2738       myLastCreatedElems.Append(newElem2);
2739       // put a new triangle on the same shape and add to the same groups
2740       if ( aShapeId )
2741         {
2742           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2743           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2744         }
2745       AddToSameGroups( newElem1, elem, aMesh );
2746       AddToSameGroups( newElem2, elem, aMesh );
2747       aMesh->RemoveElement( elem );
2748     }
2749   }
2750
2751   return true;
2752 }
2753
2754 //=======================================================================
2755 //function : getAngle
2756 //purpose  :
2757 //=======================================================================
2758
2759 double getAngle(const SMDS_MeshElement * tr1,
2760                 const SMDS_MeshElement * tr2,
2761                 const SMDS_MeshNode *    n1,
2762                 const SMDS_MeshNode *    n2)
2763 {
2764   double angle = 2. * M_PI; // bad angle
2765
2766   // get normals
2767   SMESH::Controls::TSequenceOfXYZ P1, P2;
2768   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2769        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2770     return angle;
2771   gp_Vec N1,N2;
2772   if(!tr1->IsQuadratic())
2773     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2774   else
2775     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2776   if ( N1.SquareMagnitude() <= gp::Resolution() )
2777     return angle;
2778   if(!tr2->IsQuadratic())
2779     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2780   else
2781     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2782   if ( N2.SquareMagnitude() <= gp::Resolution() )
2783     return angle;
2784
2785   // find the first diagonal node n1 in the triangles:
2786   // take in account a diagonal link orientation
2787   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2788   for ( int t = 0; t < 2; t++ ) {
2789     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2790     int i = 0, iDiag = -1;
2791     while ( it->more()) {
2792       const SMDS_MeshElement *n = it->next();
2793       if ( n == n1 || n == n2 ) {
2794         if ( iDiag < 0)
2795           iDiag = i;
2796         else {
2797           if ( i - iDiag == 1 )
2798             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2799           else
2800             nFirst[ t ] = n;
2801           break;
2802         }
2803       }
2804       i++;
2805     }
2806   }
2807   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2808     N2.Reverse();
2809
2810   angle = N1.Angle( N2 );
2811   //SCRUTE( angle );
2812   return angle;
2813 }
2814
2815 // =================================================
2816 // class generating a unique ID for a pair of nodes
2817 // and able to return nodes by that ID
2818 // =================================================
2819 class LinkID_Gen {
2820 public:
2821
2822   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2823     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2824   {}
2825
2826   long GetLinkID (const SMDS_MeshNode * n1,
2827                   const SMDS_MeshNode * n2) const
2828   {
2829     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2830   }
2831
2832   bool GetNodes (const long             theLinkID,
2833                  const SMDS_MeshNode* & theNode1,
2834                  const SMDS_MeshNode* & theNode2) const
2835   {
2836     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2837     if ( !theNode1 ) return false;
2838     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2839     if ( !theNode2 ) return false;
2840     return true;
2841   }
2842
2843 private:
2844   LinkID_Gen();
2845   const SMESHDS_Mesh* myMesh;
2846   long                myMaxID;
2847 };
2848
2849
2850 //=======================================================================
2851 //function : TriToQuad
2852 //purpose  : Fuse neighbour triangles into quadrangles.
2853 //           theCrit is used to select a neighbour to fuse with.
2854 //           theMaxAngle is a max angle between element normals at which
2855 //           fusion is still performed.
2856 //=======================================================================
2857
2858 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2859                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2860                                   const double                         theMaxAngle)
2861 {
2862   myLastCreatedElems.Clear();
2863   myLastCreatedNodes.Clear();
2864
2865   MESSAGE( "::TriToQuad()" );
2866
2867   if ( !theCrit.get() )
2868     return false;
2869
2870   SMESHDS_Mesh * aMesh = GetMeshDS();
2871
2872   // Prepare data for algo: build
2873   // 1. map of elements with their linkIDs
2874   // 2. map of linkIDs with their elements
2875
2876   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2877   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2878   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2879   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2880
2881   TIDSortedElemSet::iterator itElem;
2882   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2883   {
2884     const SMDS_MeshElement* elem = *itElem;
2885     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2886     bool IsTria = ( elem->NbCornerNodes()==3 );
2887     if (!IsTria) continue;
2888
2889     // retrieve element nodes
2890     const SMDS_MeshNode* aNodes [4];
2891     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2892     int i = 0;
2893     while ( i < 3 )
2894       aNodes[ i++ ] = itN->next();
2895     aNodes[ 3 ] = aNodes[ 0 ];
2896
2897     // fill maps
2898     for ( i = 0; i < 3; i++ ) {
2899       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2900       // check if elements sharing a link can be fused
2901       itLE = mapLi_listEl.find( link );
2902       if ( itLE != mapLi_listEl.end() ) {
2903         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2904           continue;
2905         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2906         //if ( FindShape( elem ) != FindShape( elem2 ))
2907         //  continue; // do not fuse triangles laying on different shapes
2908         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2909           continue; // avoid making badly shaped quads
2910         (*itLE).second.push_back( elem );
2911       }
2912       else {
2913         mapLi_listEl[ link ].push_back( elem );
2914       }
2915       mapEl_setLi [ elem ].insert( link );
2916     }
2917   }
2918   // Clean the maps from the links shared by a sole element, ie
2919   // links to which only one element is bound in mapLi_listEl
2920
2921   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2922     int nbElems = (*itLE).second.size();
2923     if ( nbElems < 2  ) {
2924       const SMDS_MeshElement* elem = (*itLE).second.front();
2925       SMESH_TLink link = (*itLE).first;
2926       mapEl_setLi[ elem ].erase( link );
2927       if ( mapEl_setLi[ elem ].empty() )
2928         mapEl_setLi.erase( elem );
2929     }
2930   }
2931
2932   // Algo: fuse triangles into quadrangles
2933
2934   while ( ! mapEl_setLi.empty() ) {
2935     // Look for the start element:
2936     // the element having the least nb of shared links
2937     const SMDS_MeshElement* startElem = 0;
2938     int minNbLinks = 4;
2939     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2940       int nbLinks = (*itEL).second.size();
2941       if ( nbLinks < minNbLinks ) {
2942         startElem = (*itEL).first;
2943         minNbLinks = nbLinks;
2944         if ( minNbLinks == 1 )
2945           break;
2946       }
2947     }
2948
2949     // search elements to fuse starting from startElem or links of elements
2950     // fused earlyer - startLinks
2951     list< SMESH_TLink > startLinks;
2952     while ( startElem || !startLinks.empty() ) {
2953       while ( !startElem && !startLinks.empty() ) {
2954         // Get an element to start, by a link
2955         SMESH_TLink linkId = startLinks.front();
2956         startLinks.pop_front();
2957         itLE = mapLi_listEl.find( linkId );
2958         if ( itLE != mapLi_listEl.end() ) {
2959           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2960           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2961           for ( ; itE != listElem.end() ; itE++ )
2962             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2963               startElem = (*itE);
2964           mapLi_listEl.erase( itLE );
2965         }
2966       }
2967
2968       if ( startElem ) {
2969         // Get candidates to be fused
2970         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2971         const SMESH_TLink *link12, *link13;
2972         startElem = 0;
2973         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2974         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2975         ASSERT( !setLi.empty() );
2976         set< SMESH_TLink >::iterator itLi;
2977         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2978         {
2979           const SMESH_TLink & link = (*itLi);
2980           itLE = mapLi_listEl.find( link );
2981           if ( itLE == mapLi_listEl.end() )
2982             continue;
2983
2984           const SMDS_MeshElement* elem = (*itLE).second.front();
2985           if ( elem == tr1 )
2986             elem = (*itLE).second.back();
2987           mapLi_listEl.erase( itLE );
2988           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2989             continue;
2990           if ( tr2 ) {
2991             tr3 = elem;
2992             link13 = &link;
2993           }
2994           else {
2995             tr2 = elem;
2996             link12 = &link;
2997           }
2998
2999           // add other links of elem to list of links to re-start from
3000           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3001           set< SMESH_TLink >::iterator it;
3002           for ( it = links.begin(); it != links.end(); it++ ) {
3003             const SMESH_TLink& link2 = (*it);
3004             if ( link2 != link )
3005               startLinks.push_back( link2 );
3006           }
3007         }
3008
3009         // Get nodes of possible quadrangles
3010         const SMDS_MeshNode *n12 [4], *n13 [4];
3011         bool Ok12 = false, Ok13 = false;
3012         const SMDS_MeshNode *linkNode1, *linkNode2;
3013         if(tr2) {
3014           linkNode1 = link12->first;
3015           linkNode2 = link12->second;
3016           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3017             Ok12 = true;
3018         }
3019         if(tr3) {
3020           linkNode1 = link13->first;
3021           linkNode2 = link13->second;
3022           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3023             Ok13 = true;
3024         }
3025
3026         // Choose a pair to fuse
3027         if ( Ok12 && Ok13 ) {
3028           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3029           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3030           double aBadRate12 = getBadRate( &quad12, theCrit );
3031           double aBadRate13 = getBadRate( &quad13, theCrit );
3032           if (  aBadRate13 < aBadRate12 )
3033             Ok12 = false;
3034           else
3035             Ok13 = false;
3036         }
3037
3038         // Make quadrangles
3039         // and remove fused elems and remove links from the maps
3040         mapEl_setLi.erase( tr1 );
3041         if ( Ok12 )
3042         {
3043           mapEl_setLi.erase( tr2 );
3044           mapLi_listEl.erase( *link12 );
3045           if ( tr1->NbNodes() == 3 )
3046           {
3047             const SMDS_MeshElement* newElem = 0;
3048             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3049             myLastCreatedElems.Append(newElem);
3050             AddToSameGroups( newElem, tr1, aMesh );
3051             int aShapeId = tr1->getshapeId();
3052             if ( aShapeId )
3053               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3054             aMesh->RemoveElement( tr1 );
3055             aMesh->RemoveElement( tr2 );
3056           }
3057           else {
3058             vector< const SMDS_MeshNode* > N1;
3059             vector< const SMDS_MeshNode* > N2;
3060             getNodesFromTwoTria(tr1,tr2,N1,N2);
3061             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3062             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3063             // i.e. first nodes from both arrays form a new diagonal
3064             const SMDS_MeshNode* aNodes[8];
3065             aNodes[0] = N1[0];
3066             aNodes[1] = N1[1];
3067             aNodes[2] = N2[0];
3068             aNodes[3] = N2[1];
3069             aNodes[4] = N1[3];
3070             aNodes[5] = N2[5];
3071             aNodes[6] = N2[3];
3072             aNodes[7] = N1[5];
3073             const SMDS_MeshElement* newElem = 0;
3074             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3075               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3076                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3077             else
3078               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3079                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3080             myLastCreatedElems.Append(newElem);
3081             AddToSameGroups( newElem, tr1, aMesh );
3082             int aShapeId = tr1->getshapeId();
3083             if ( aShapeId )
3084               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3085             aMesh->RemoveElement( tr1 );
3086             aMesh->RemoveElement( tr2 );
3087             // remove middle node (9)
3088             if ( N1[4]->NbInverseElements() == 0 )
3089               aMesh->RemoveNode( N1[4] );
3090             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3091               aMesh->RemoveNode( N1[6] );
3092             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3093               aMesh->RemoveNode( N2[6] );
3094           }
3095         }
3096         else if ( Ok13 )
3097         {
3098           mapEl_setLi.erase( tr3 );
3099           mapLi_listEl.erase( *link13 );
3100           if ( tr1->NbNodes() == 3 ) {
3101             const SMDS_MeshElement* newElem = 0;
3102             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3103             myLastCreatedElems.Append(newElem);
3104             AddToSameGroups( newElem, tr1, aMesh );
3105             int aShapeId = tr1->getshapeId();
3106             if ( aShapeId )
3107               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3108             aMesh->RemoveElement( tr1 );
3109             aMesh->RemoveElement( tr3 );
3110           }
3111           else {
3112             vector< const SMDS_MeshNode* > N1;
3113             vector< const SMDS_MeshNode* > N2;
3114             getNodesFromTwoTria(tr1,tr3,N1,N2);
3115             // now we receive following N1 and N2 (using numeration as above image)
3116             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3117             // i.e. first nodes from both arrays form a new diagonal
3118             const SMDS_MeshNode* aNodes[8];
3119             aNodes[0] = N1[0];
3120             aNodes[1] = N1[1];
3121             aNodes[2] = N2[0];
3122             aNodes[3] = N2[1];
3123             aNodes[4] = N1[3];
3124             aNodes[5] = N2[5];
3125             aNodes[6] = N2[3];
3126             aNodes[7] = N1[5];
3127             const SMDS_MeshElement* newElem = 0;
3128             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3129               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3130                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3131             else
3132               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3133                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3134             myLastCreatedElems.Append(newElem);
3135             AddToSameGroups( newElem, tr1, aMesh );
3136             int aShapeId = tr1->getshapeId();
3137             if ( aShapeId )
3138               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3139             aMesh->RemoveElement( tr1 );
3140             aMesh->RemoveElement( tr3 );
3141             // remove middle node (9)
3142             if ( N1[4]->NbInverseElements() == 0 )
3143               aMesh->RemoveNode( N1[4] );
3144             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3145               aMesh->RemoveNode( N1[6] );
3146             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3147               aMesh->RemoveNode( N2[6] );
3148           }
3149         }
3150
3151         // Next element to fuse: the rejected one
3152         if ( tr3 )
3153           startElem = Ok12 ? tr3 : tr2;
3154
3155       } // if ( startElem )
3156     } // while ( startElem || !startLinks.empty() )
3157   } // while ( ! mapEl_setLi.empty() )
3158
3159   return true;
3160 }
3161
3162
3163 /*#define DUMPSO(txt) \
3164 //  cout << txt << endl;
3165 //=============================================================================
3166 //
3167 //
3168 //
3169 //=============================================================================
3170 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3171 {
3172 if ( i1 == i2 )
3173 return;
3174 int tmp = idNodes[ i1 ];
3175 idNodes[ i1 ] = idNodes[ i2 ];
3176 idNodes[ i2 ] = tmp;
3177 gp_Pnt Ptmp = P[ i1 ];
3178 P[ i1 ] = P[ i2 ];
3179 P[ i2 ] = Ptmp;
3180 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3181 }
3182
3183 //=======================================================================
3184 //function : SortQuadNodes
3185 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3186 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3187 //           1 or 2 else 0.
3188 //=======================================================================
3189
3190 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3191 int               idNodes[] )
3192 {
3193   gp_Pnt P[4];
3194   int i;
3195   for ( i = 0; i < 4; i++ ) {
3196     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3197     if ( !n ) return 0;
3198     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3199   }
3200
3201   gp_Vec V1(P[0], P[1]);
3202   gp_Vec V2(P[0], P[2]);
3203   gp_Vec V3(P[0], P[3]);
3204
3205   gp_Vec Cross1 = V1 ^ V2;
3206   gp_Vec Cross2 = V2 ^ V3;
3207
3208   i = 0;
3209   if (Cross1.Dot(Cross2) < 0)
3210   {
3211     Cross1 = V2 ^ V1;
3212     Cross2 = V1 ^ V3;
3213
3214     if (Cross1.Dot(Cross2) < 0)
3215       i = 2;
3216     else
3217       i = 1;
3218     swap ( i, i + 1, idNodes, P );
3219
3220     //     for ( int ii = 0; ii < 4; ii++ ) {
3221     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3222     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3223     //     }
3224   }
3225   return i;
3226 }
3227
3228 //=======================================================================
3229 //function : SortHexaNodes
3230 //purpose  : Set 8 nodes of a hexahedron in a good order.
3231 //           Return success status
3232 //=======================================================================
3233
3234 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3235                                       int               idNodes[] )
3236 {
3237   gp_Pnt P[8];
3238   int i;
3239   DUMPSO( "INPUT: ========================================");
3240   for ( i = 0; i < 8; i++ ) {
3241     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3242     if ( !n ) return false;
3243     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3244     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3245   }
3246   DUMPSO( "========================================");
3247
3248
3249   set<int> faceNodes;  // ids of bottom face nodes, to be found
3250   set<int> checkedId1; // ids of tried 2-nd nodes
3251   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3252   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3253   int iMin, iLoop1 = 0;
3254
3255   // Loop to try the 2-nd nodes
3256
3257   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3258   {
3259     // Find not checked 2-nd node
3260     for ( i = 1; i < 8; i++ )
3261       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3262         int id1 = idNodes[i];
3263         swap ( 1, i, idNodes, P );
3264         checkedId1.insert ( id1 );
3265         break;
3266       }
3267
3268     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3269     // ie that all but meybe one (id3 which is on the same face) nodes
3270     // lay on the same side from the triangle plane.
3271
3272     bool manyInPlane = false; // more than 4 nodes lay in plane
3273     int iLoop2 = 0;
3274     while ( ++iLoop2 < 6 ) {
3275
3276       // get 1-2-3 plane coeffs
3277       Standard_Real A, B, C, D;
3278       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3279       if ( N.SquareMagnitude() > gp::Resolution() )
3280       {
3281         gp_Pln pln ( P[0], N );
3282         pln.Coefficients( A, B, C, D );
3283
3284         // find the node (iMin) closest to pln
3285         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3286         set<int> idInPln;
3287         for ( i = 3; i < 8; i++ ) {
3288           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3289           if ( fabs( dist[i] ) < minDist ) {
3290             minDist = fabs( dist[i] );
3291             iMin = i;
3292           }
3293           if ( fabs( dist[i] ) <= tol )
3294             idInPln.insert( idNodes[i] );
3295         }
3296
3297         // there should not be more than 4 nodes in bottom plane
3298         if ( idInPln.size() > 1 )
3299         {
3300           DUMPSO( "### idInPln.size() = " << idInPln.size());
3301           // idInPlane does not contain the first 3 nodes
3302           if ( manyInPlane || idInPln.size() == 5)
3303             return false; // all nodes in one plane
3304           manyInPlane = true;
3305
3306           // set the 1-st node to be not in plane
3307           for ( i = 3; i < 8; i++ ) {
3308             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3309               DUMPSO( "### Reset 0-th node");
3310               swap( 0, i, idNodes, P );
3311               break;
3312             }
3313           }
3314
3315           // reset to re-check second nodes
3316           leastDist = DBL_MAX;
3317           faceNodes.clear();
3318           checkedId1.clear();
3319           iLoop1 = 0;
3320           break; // from iLoop2;
3321         }
3322
3323         // check that the other 4 nodes are on the same side
3324         bool sameSide = true;
3325         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3326         for ( i = 3; sameSide && i < 8; i++ ) {
3327           if ( i != iMin )
3328             sameSide = ( isNeg == dist[i] <= 0.);
3329         }
3330
3331         // keep best solution
3332         if ( sameSide && minDist < leastDist ) {
3333           leastDist = minDist;
3334           faceNodes.clear();
3335           faceNodes.insert( idNodes[ 1 ] );
3336           faceNodes.insert( idNodes[ 2 ] );
3337           faceNodes.insert( idNodes[ iMin ] );
3338           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3339                   << " leastDist = " << leastDist);
3340           if ( leastDist <= DBL_MIN )
3341             break;
3342         }
3343       }
3344
3345       // set next 3-d node to check
3346       int iNext = 2 + iLoop2;
3347       if ( iNext < 8 ) {
3348         DUMPSO( "Try 2-nd");
3349         swap ( 2, iNext, idNodes, P );
3350       }
3351     } // while ( iLoop2 < 6 )
3352   } // iLoop1
3353
3354   if ( faceNodes.empty() ) return false;
3355
3356   // Put the faceNodes in proper places
3357   for ( i = 4; i < 8; i++ ) {
3358     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3359       // find a place to put
3360       int iTo = 1;
3361       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3362         iTo++;
3363       DUMPSO( "Set faceNodes");
3364       swap ( iTo, i, idNodes, P );
3365     }
3366   }
3367
3368
3369   // Set nodes of the found bottom face in good order
3370   DUMPSO( " Found bottom face: ");
3371   i = SortQuadNodes( theMesh, idNodes );
3372   if ( i ) {
3373     gp_Pnt Ptmp = P[ i ];
3374     P[ i ] = P[ i+1 ];
3375     P[ i+1 ] = Ptmp;
3376   }
3377   //   else
3378   //     for ( int ii = 0; ii < 4; ii++ ) {
3379   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3380   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3381   //    }
3382
3383   // Gravity center of the top and bottom faces
3384   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3385   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3386
3387   // Get direction from the bottom to the top face
3388   gp_Vec upDir ( aGCb, aGCt );
3389   Standard_Real upDirSize = upDir.Magnitude();
3390   if ( upDirSize <= gp::Resolution() ) return false;
3391   upDir / upDirSize;
3392
3393   // Assure that the bottom face normal points up
3394   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3395   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3396   if ( Nb.Dot( upDir ) < 0 ) {
3397     DUMPSO( "Reverse bottom face");
3398     swap( 1, 3, idNodes, P );
3399   }
3400
3401   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3402   Standard_Real minDist = DBL_MAX;
3403   for ( i = 4; i < 8; i++ ) {
3404     // projection of P[i] to the plane defined by P[0] and upDir
3405     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3406     Standard_Real sqDist = P[0].SquareDistance( Pp );
3407     if ( sqDist < minDist ) {
3408       minDist = sqDist;
3409       iMin = i;
3410     }
3411   }
3412   DUMPSO( "Set 4-th");
3413   swap ( 4, iMin, idNodes, P );
3414
3415   // Set nodes of the top face in good order
3416   DUMPSO( "Sort top face");
3417   i = SortQuadNodes( theMesh, &idNodes[4] );
3418   if ( i ) {
3419     i += 4;
3420     gp_Pnt Ptmp = P[ i ];
3421     P[ i ] = P[ i+1 ];
3422     P[ i+1 ] = Ptmp;
3423   }
3424
3425   // Assure that direction of the top face normal is from the bottom face
3426   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3427   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3428   if ( Nt.Dot( upDir ) < 0 ) {
3429     DUMPSO( "Reverse top face");
3430     swap( 5, 7, idNodes, P );
3431   }
3432
3433   //   DUMPSO( "OUTPUT: ========================================");
3434   //   for ( i = 0; i < 8; i++ ) {
3435   //     float *p = ugrid->GetPoint(idNodes[i]);
3436   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3437   //   }
3438
3439   return true;
3440 }*/
3441
3442 //================================================================================
3443 /*!
3444  * \brief Return nodes linked to the given one
3445  * \param theNode - the node
3446  * \param linkedNodes - the found nodes
3447  * \param type - the type of elements to check
3448  *
3449  * Medium nodes are ignored
3450  */
3451 //================================================================================
3452
3453 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3454                                        TIDSortedElemSet &   linkedNodes,
3455                                        SMDSAbs_ElementType  type )
3456 {
3457   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3458   while ( elemIt->more() )
3459   {
3460     const SMDS_MeshElement* elem = elemIt->next();
3461     if(elem->GetType() == SMDSAbs_0DElement)
3462       continue;
3463
3464     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3465     if ( elem->GetType() == SMDSAbs_Volume )
3466     {
3467       SMDS_VolumeTool vol( elem );
3468       while ( nodeIt->more() ) {
3469         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3470         if ( theNode != n && vol.IsLinked( theNode, n ))
3471           linkedNodes.insert( n );
3472       }
3473     }
3474     else
3475     {
3476       for ( int i = 0; nodeIt->more(); ++i ) {
3477         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3478         if ( n == theNode ) {
3479           int iBefore = i - 1;
3480           int iAfter  = i + 1;
3481           if ( elem->IsQuadratic() ) {
3482             int nb = elem->NbNodes() / 2;
3483             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3484             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3485           }
3486           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3487           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3488         }
3489       }
3490     }
3491   }
3492 }
3493
3494 //=======================================================================
3495 //function : laplacianSmooth
3496 //purpose  : pulls theNode toward the center of surrounding nodes directly
3497 //           connected to that node along an element edge
3498 //=======================================================================
3499
3500 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3501                      const Handle(Geom_Surface)&          theSurface,
3502                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3503 {
3504   // find surrounding nodes
3505
3506   TIDSortedElemSet nodeSet;
3507   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3508
3509   // compute new coodrs
3510
3511   double coord[] = { 0., 0., 0. };
3512   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3513   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3514     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3515     if ( theSurface.IsNull() ) { // smooth in 3D
3516       coord[0] += node->X();
3517       coord[1] += node->Y();
3518       coord[2] += node->Z();
3519     }
3520     else { // smooth in 2D
3521       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3522       gp_XY* uv = theUVMap[ node ];
3523       coord[0] += uv->X();
3524       coord[1] += uv->Y();
3525     }
3526   }
3527   int nbNodes = nodeSet.size();
3528   if ( !nbNodes )
3529     return;
3530   coord[0] /= nbNodes;
3531   coord[1] /= nbNodes;
3532
3533   if ( !theSurface.IsNull() ) {
3534     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3535     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3536     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3537     coord[0] = p3d.X();
3538     coord[1] = p3d.Y();
3539     coord[2] = p3d.Z();
3540   }
3541   else
3542     coord[2] /= nbNodes;
3543
3544   // move node
3545
3546   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3547 }
3548
3549 //=======================================================================
3550 //function : centroidalSmooth
3551 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3552 //           surrounding elements
3553 //=======================================================================
3554
3555 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3556                       const Handle(Geom_Surface)&          theSurface,
3557                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3558 {
3559   gp_XYZ aNewXYZ(0.,0.,0.);
3560   SMESH::Controls::Area anAreaFunc;
3561   double totalArea = 0.;
3562   int nbElems = 0;
3563
3564   // compute new XYZ
3565
3566   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3567   while ( elemIt->more() )
3568   {
3569     const SMDS_MeshElement* elem = elemIt->next();
3570     nbElems++;
3571
3572     gp_XYZ elemCenter(0.,0.,0.);
3573     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3574     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3575     int nn = elem->NbNodes();
3576     if(elem->IsQuadratic()) nn = nn/2;
3577     int i=0;
3578     //while ( itN->more() ) {
3579     while ( i<nn ) {
3580       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3581       i++;
3582       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3583       aNodePoints.push_back( aP );
3584       if ( !theSurface.IsNull() ) { // smooth in 2D
3585         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3586         gp_XY* uv = theUVMap[ aNode ];
3587         aP.SetCoord( uv->X(), uv->Y(), 0. );
3588       }
3589       elemCenter += aP;
3590     }
3591     double elemArea = anAreaFunc.GetValue( aNodePoints );
3592     totalArea += elemArea;
3593     elemCenter /= nn;
3594     aNewXYZ += elemCenter * elemArea;
3595   }
3596   aNewXYZ /= totalArea;
3597   if ( !theSurface.IsNull() ) {
3598     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3599     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3600   }
3601
3602   // move node
3603
3604   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3605 }
3606
3607 //=======================================================================
3608 //function : getClosestUV
3609 //purpose  : return UV of closest projection
3610 //=======================================================================
3611
3612 static bool getClosestUV (Extrema_GenExtPS& projector,
3613                           const gp_Pnt&     point,
3614                           gp_XY &           result)
3615 {
3616   projector.Perform( point );
3617   if ( projector.IsDone() ) {
3618     double u, v, minVal = DBL_MAX;
3619     for ( int i = projector.NbExt(); i > 0; i-- )
3620 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3621       if ( projector.SquareDistance( i ) < minVal ) {
3622         minVal = projector.SquareDistance( i );
3623 #else
3624       if ( projector.Value( i ) < minVal ) {
3625         minVal = projector.Value( i );
3626 #endif
3627         projector.Point( i ).Parameter( u, v );
3628       }
3629     result.SetCoord( u, v );
3630     return true;
3631   }
3632   return false;
3633 }
3634
3635 //=======================================================================
3636 //function : Smooth
3637 //purpose  : Smooth theElements during theNbIterations or until a worst
3638 //           element has aspect ratio <= theTgtAspectRatio.
3639 //           Aspect Ratio varies in range [1.0, inf].
3640 //           If theElements is empty, the whole mesh is smoothed.
3641 //           theFixedNodes contains additionally fixed nodes. Nodes built
3642 //           on edges and boundary nodes are always fixed.
3643 //=======================================================================
3644
3645 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3646                                set<const SMDS_MeshNode*> & theFixedNodes,
3647                                const SmoothMethod          theSmoothMethod,
3648                                const int                   theNbIterations,
3649                                double                      theTgtAspectRatio,
3650                                const bool                  the2D)
3651 {
3652   myLastCreatedElems.Clear();
3653   myLastCreatedNodes.Clear();
3654
3655   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3656
3657   if ( theTgtAspectRatio < 1.0 )
3658     theTgtAspectRatio = 1.0;
3659
3660   const double disttol = 1.e-16;
3661
3662   SMESH::Controls::AspectRatio aQualityFunc;
3663
3664   SMESHDS_Mesh* aMesh = GetMeshDS();
3665
3666   if ( theElems.empty() ) {
3667     // add all faces to theElems
3668     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3669     while ( fIt->more() ) {
3670       const SMDS_MeshElement* face = fIt->next();
3671       theElems.insert( theElems.end(), face );
3672     }
3673   }
3674   // get all face ids theElems are on
3675   set< int > faceIdSet;
3676   TIDSortedElemSet::iterator itElem;
3677   if ( the2D )
3678     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3679       int fId = FindShape( *itElem );
3680       // check that corresponding submesh exists and a shape is face
3681       if (fId &&
3682           faceIdSet.find( fId ) == faceIdSet.end() &&
3683           aMesh->MeshElements( fId )) {
3684         TopoDS_Shape F = aMesh->IndexToShape( fId );
3685         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3686           faceIdSet.insert( fId );
3687       }
3688     }
3689   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3690
3691   // ===============================================
3692   // smooth elements on each TopoDS_Face separately
3693   // ===============================================
3694
3695   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3696   for ( ; fId != faceIdSet.rend(); ++fId ) {
3697     // get face surface and submesh
3698     Handle(Geom_Surface) surface;
3699     SMESHDS_SubMesh* faceSubMesh = 0;
3700     TopoDS_Face face;
3701     double fToler2 = 0, f,l;
3702     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3703     bool isUPeriodic = false, isVPeriodic = false;
3704     if ( *fId ) {
3705       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3706       surface = BRep_Tool::Surface( face );
3707       faceSubMesh = aMesh->MeshElements( *fId );
3708       fToler2 = BRep_Tool::Tolerance( face );
3709       fToler2 *= fToler2 * 10.;
3710       isUPeriodic = surface->IsUPeriodic();
3711       if ( isUPeriodic )
3712         surface->UPeriod();
3713       isVPeriodic = surface->IsVPeriodic();
3714       if ( isVPeriodic )
3715         surface->VPeriod();
3716       surface->Bounds( u1, u2, v1, v2 );
3717     }
3718     // ---------------------------------------------------------
3719     // for elements on a face, find movable and fixed nodes and
3720     // compute UV for them
3721     // ---------------------------------------------------------
3722     bool checkBoundaryNodes = false;
3723     bool isQuadratic = false;
3724     set<const SMDS_MeshNode*> setMovableNodes;
3725     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3726     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3727     list< const SMDS_MeshElement* > elemsOnFace;
3728
3729     Extrema_GenExtPS projector;
3730     GeomAdaptor_Surface surfAdaptor;
3731     if ( !surface.IsNull() ) {
3732       surfAdaptor.Load( surface );
3733       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3734     }
3735     int nbElemOnFace = 0;
3736     itElem = theElems.begin();
3737     // loop on not yet smoothed elements: look for elems on a face
3738     while ( itElem != theElems.end() ) {
3739       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3740         break; // all elements found
3741
3742       const SMDS_MeshElement* elem = *itElem;
3743       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3744            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3745         ++itElem;
3746         continue;
3747       }
3748       elemsOnFace.push_back( elem );
3749       theElems.erase( itElem++ );
3750       nbElemOnFace++;
3751
3752       if ( !isQuadratic )
3753         isQuadratic = elem->IsQuadratic();
3754
3755       // get movable nodes of elem
3756       const SMDS_MeshNode* node;
3757       SMDS_TypeOfPosition posType;
3758       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3759       int nn = 0, nbn =  elem->NbNodes();
3760       if(elem->IsQuadratic())
3761         nbn = nbn/2;
3762       while ( nn++ < nbn ) {
3763         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3764         const SMDS_PositionPtr& pos = node->GetPosition();
3765         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3766         if (posType != SMDS_TOP_EDGE &&
3767             posType != SMDS_TOP_VERTEX &&
3768             theFixedNodes.find( node ) == theFixedNodes.end())
3769         {
3770           // check if all faces around the node are on faceSubMesh
3771           // because a node on edge may be bound to face
3772           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3773           bool all = true;
3774           if ( faceSubMesh ) {
3775             while ( eIt->more() && all ) {
3776               const SMDS_MeshElement* e = eIt->next();
3777               all = faceSubMesh->Contains( e );
3778             }
3779           }
3780           if ( all )
3781             setMovableNodes.insert( node );
3782           else
3783             checkBoundaryNodes = true;
3784         }
3785         if ( posType == SMDS_TOP_3DSPACE )
3786           checkBoundaryNodes = true;
3787       }
3788
3789       if ( surface.IsNull() )
3790         continue;
3791
3792       // get nodes to check UV
3793       list< const SMDS_MeshNode* > uvCheckNodes;
3794       itN = elem->nodesIterator();
3795       nn = 0; nbn =  elem->NbNodes();
3796       if(elem->IsQuadratic())
3797         nbn = nbn/2;
3798       while ( nn++ < nbn ) {
3799         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3800         if ( uvMap.find( node ) == uvMap.end() )
3801           uvCheckNodes.push_back( node );
3802         // add nodes of elems sharing node
3803         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3804         //         while ( eIt->more() ) {
3805         //           const SMDS_MeshElement* e = eIt->next();
3806         //           if ( e != elem ) {
3807         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3808         //             while ( nIt->more() ) {
3809         //               const SMDS_MeshNode* n =
3810         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3811         //               if ( uvMap.find( n ) == uvMap.end() )
3812         //                 uvCheckNodes.push_back( n );
3813         //             }
3814         //           }
3815         //         }
3816       }
3817       // check UV on face
3818       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3819       for ( ; n != uvCheckNodes.end(); ++n ) {
3820         node = *n;
3821         gp_XY uv( 0, 0 );
3822         const SMDS_PositionPtr& pos = node->GetPosition();
3823         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3824         // get existing UV
3825         switch ( posType ) {
3826         case SMDS_TOP_FACE: {
3827           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3828           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3829           break;
3830         }
3831         case SMDS_TOP_EDGE: {
3832           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3833           Handle(Geom2d_Curve) pcurve;
3834           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3835             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3836           if ( !pcurve.IsNull() ) {
3837             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3838             uv = pcurve->Value( u ).XY();
3839           }
3840           break;
3841         }
3842         case SMDS_TOP_VERTEX: {
3843           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3844           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3845             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3846           break;
3847         }
3848         default:;
3849         }
3850         // check existing UV
3851         bool project = true;
3852         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3853         double dist1 = DBL_MAX, dist2 = 0;
3854         if ( posType != SMDS_TOP_3DSPACE ) {
3855           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3856           project = dist1 > fToler2;
3857         }
3858         if ( project ) { // compute new UV
3859           gp_XY newUV;
3860           if ( !getClosestUV( projector, pNode, newUV )) {
3861             MESSAGE("Node Projection Failed " << node);
3862           }
3863           else {
3864             if ( isUPeriodic )
3865               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3866             if ( isVPeriodic )
3867               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3868             // check new UV
3869             if ( posType != SMDS_TOP_3DSPACE )
3870               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3871             if ( dist2 < dist1 )
3872               uv = newUV;
3873           }
3874         }
3875         // store UV in the map
3876         listUV.push_back( uv );
3877         uvMap.insert( make_pair( node, &listUV.back() ));
3878       }
3879     } // loop on not yet smoothed elements
3880
3881     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3882       checkBoundaryNodes = true;
3883
3884     // fix nodes on mesh boundary
3885
3886     if ( checkBoundaryNodes ) {
3887       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3888       map< SMESH_TLink, int >::iterator link_nb;
3889       // put all elements links to linkNbMap
3890       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3891       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3892         const SMDS_MeshElement* elem = (*elemIt);
3893         int nbn =  elem->NbCornerNodes();
3894         // loop on elem links: insert them in linkNbMap
3895         for ( int iN = 0; iN < nbn; ++iN ) {
3896           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3897           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3898           SMESH_TLink link( n1, n2 );
3899           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3900           link_nb->second++;
3901         }
3902       }
3903       // remove nodes that are in links encountered only once from setMovableNodes
3904       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3905         if ( link_nb->second == 1 ) {
3906           setMovableNodes.erase( link_nb->first.node1() );
3907           setMovableNodes.erase( link_nb->first.node2() );
3908         }
3909       }
3910     }
3911
3912     // -----------------------------------------------------
3913     // for nodes on seam edge, compute one more UV ( uvMap2 );
3914     // find movable nodes linked to nodes on seam and which
3915     // are to be smoothed using the second UV ( uvMap2 )
3916     // -----------------------------------------------------
3917
3918     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3919     if ( !surface.IsNull() ) {
3920       TopExp_Explorer eExp( face, TopAbs_EDGE );
3921       for ( ; eExp.More(); eExp.Next() ) {
3922         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3923         if ( !BRep_Tool::IsClosed( edge, face ))
3924           continue;
3925         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3926         if ( !sm ) continue;
3927         // find out which parameter varies for a node on seam
3928         double f,l;
3929         gp_Pnt2d uv1, uv2;
3930         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3931         if ( pcurve.IsNull() ) continue;
3932         uv1 = pcurve->Value( f );
3933         edge.Reverse();
3934         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3935         if ( pcurve.IsNull() ) continue;
3936         uv2 = pcurve->Value( f );
3937         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3938         // assure uv1 < uv2
3939         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3940           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3941         }
3942         // get nodes on seam and its vertices
3943         list< const SMDS_MeshNode* > seamNodes;
3944         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3945         while ( nSeamIt->more() ) {
3946           const SMDS_MeshNode* node = nSeamIt->next();
3947           if ( !isQuadratic || !IsMedium( node ))
3948             seamNodes.push_back( node );
3949         }
3950         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3951         for ( ; vExp.More(); vExp.Next() ) {
3952           sm = aMesh->MeshElements( vExp.Current() );
3953           if ( sm ) {
3954             nSeamIt = sm->GetNodes();
3955             while ( nSeamIt->more() )
3956               seamNodes.push_back( nSeamIt->next() );
3957           }
3958         }
3959         // loop on nodes on seam
3960         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3961         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3962           const SMDS_MeshNode* nSeam = *noSeIt;
3963           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3964           if ( n_uv == uvMap.end() )
3965             continue;
3966           // set the first UV
3967           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3968           // set the second UV
3969           listUV.push_back( *n_uv->second );
3970           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3971           if ( uvMap2.empty() )
3972             uvMap2 = uvMap; // copy the uvMap contents
3973           uvMap2[ nSeam ] = &listUV.back();
3974
3975           // collect movable nodes linked to ones on seam in nodesNearSeam
3976           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3977           while ( eIt->more() ) {
3978             const SMDS_MeshElement* e = eIt->next();
3979             int nbUseMap1 = 0, nbUseMap2 = 0;
3980             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3981             int nn = 0, nbn =  e->NbNodes();
3982             if(e->IsQuadratic()) nbn = nbn/2;
3983             while ( nn++ < nbn )
3984             {
3985               const SMDS_MeshNode* n =
3986                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3987               if (n == nSeam ||
3988                   setMovableNodes.find( n ) == setMovableNodes.end() )
3989                 continue;
3990               // add only nodes being closer to uv2 than to uv1
3991               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3992                            0.5 * ( n->Y() + nSeam->Y() ),
3993                            0.5 * ( n->Z() + nSeam->Z() ));
3994               gp_XY uv;
3995               getClosestUV( projector, pMid, uv );
3996               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3997                 nodesNearSeam.insert( n );
3998                 nbUseMap2++;
3999               }
4000               else
4001                 nbUseMap1++;
4002             }
4003             // for centroidalSmooth all element nodes must
4004             // be on one side of a seam
4005             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4006               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4007               nn = 0;
4008               while ( nn++ < nbn ) {
4009                 const SMDS_MeshNode* n =
4010                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4011                 setMovableNodes.erase( n );
4012               }
4013             }
4014           }
4015         } // loop on nodes on seam
4016       } // loop on edge of a face
4017     } // if ( !face.IsNull() )
4018
4019     if ( setMovableNodes.empty() ) {
4020       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4021       continue; // goto next face
4022     }
4023
4024     // -------------
4025     // SMOOTHING //
4026     // -------------
4027
4028     int it = -1;
4029     double maxRatio = -1., maxDisplacement = -1.;
4030     set<const SMDS_MeshNode*>::iterator nodeToMove;
4031     for ( it = 0; it < theNbIterations; it++ ) {
4032       maxDisplacement = 0.;
4033       nodeToMove = setMovableNodes.begin();
4034       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4035         const SMDS_MeshNode* node = (*nodeToMove);
4036         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4037
4038         // smooth
4039         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4040         if ( theSmoothMethod == LAPLACIAN )
4041           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4042         else
4043           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4044
4045         // node displacement
4046         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4047         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4048         if ( aDispl > maxDisplacement )
4049           maxDisplacement = aDispl;
4050       }
4051       // no node movement => exit
4052       //if ( maxDisplacement < 1.e-16 ) {
4053       if ( maxDisplacement < disttol ) {
4054         MESSAGE("-- no node movement --");
4055         break;
4056       }
4057
4058       // check elements quality
4059       maxRatio  = 0;
4060       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4062         const SMDS_MeshElement* elem = (*elemIt);
4063         if ( !elem || elem->GetType() != SMDSAbs_Face )
4064           continue;
4065         SMESH::Controls::TSequenceOfXYZ aPoints;
4066         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4067           double aValue = aQualityFunc.GetValue( aPoints );
4068           if ( aValue > maxRatio )
4069             maxRatio = aValue;
4070         }
4071       }
4072       if ( maxRatio <= theTgtAspectRatio ) {
4073         MESSAGE("-- quality achived --");
4074         break;
4075       }
4076       if (it+1 == theNbIterations) {
4077         MESSAGE("-- Iteration limit exceeded --");
4078       }
4079     } // smoothing iterations
4080
4081     MESSAGE(" Face id: " << *fId <<
4082             " Nb iterstions: " << it <<
4083             " Displacement: " << maxDisplacement <<
4084             " Aspect Ratio " << maxRatio);
4085
4086     // ---------------------------------------
4087     // new nodes positions are computed,
4088     // record movement in DS and set new UV
4089     // ---------------------------------------
4090     nodeToMove = setMovableNodes.begin();
4091     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4092       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4093       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4094       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4095       if ( node_uv != uvMap.end() ) {
4096         gp_XY* uv = node_uv->second;
4097         node->SetPosition
4098           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4099       }
4100     }
4101
4102     // move medium nodes of quadratic elements
4103     if ( isQuadratic )
4104     {
4105       SMESH_MesherHelper helper( *GetMesh() );
4106       helper.SetSubShape( face );
4107       vector<const SMDS_MeshNode*> nodes;
4108       bool checkUV;
4109       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4110       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4111       {
4112         const SMDS_MeshElement* QF = *elemIt;
4113         if ( QF->IsQuadratic() )
4114         {
4115           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4116                         SMDS_MeshElement::iterator() );
4117           nodes.push_back( nodes[0] );
4118           gp_Pnt xyz;
4119           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4120           {
4121             if ( !surface.IsNull() )
4122             {
4123               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4124               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4125               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4126               xyz = surface->Value( uv.X(), uv.Y() );
4127             }
4128             else {
4129               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4130             }
4131             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4132               // we have to move a medium node
4133               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4134           }
4135         }
4136       }
4137     }
4138
4139   } // loop on face ids
4140
4141 }
4142
4143 //=======================================================================
4144 //function : isReverse
4145 //purpose  : Return true if normal of prevNodes is not co-directied with
4146 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4147 //           iNotSame is where prevNodes and nextNodes are different.
4148 //           If result is true then future volume orientation is OK
4149 //=======================================================================
4150
4151 static bool isReverse(const SMDS_MeshElement*             face,
4152                       const vector<const SMDS_MeshNode*>& prevNodes,
4153                       const vector<const SMDS_MeshNode*>& nextNodes,
4154                       const int                           iNotSame)
4155 {
4156
4157   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4158   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4159   gp_XYZ extrDir( pN - pP ), faceNorm;
4160   SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4161
4162   return faceNorm * extrDir < 0.0;
4163 }
4164
4165 //=======================================================================
4166 /*!
4167  * \brief Create elements by sweeping an element
4168  * \param elem - element to sweep
4169  * \param newNodesItVec - nodes generated from each node of the element
4170  * \param newElems - generated elements
4171  * \param nbSteps - number of sweeping steps
4172  * \param srcElements - to append elem for each generated element
4173  */
4174 //=======================================================================
4175
4176 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4177                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4178                                     list<const SMDS_MeshElement*>&        newElems,
4179                                     const int                             nbSteps,
4180                                     SMESH_SequenceOfElemPtr&              srcElements)
4181 {
4182   //MESSAGE("sweepElement " << nbSteps);
4183   SMESHDS_Mesh* aMesh = GetMeshDS();
4184
4185   const int           nbNodes = elem->NbNodes();
4186   const int         nbCorners = elem->NbCornerNodes();
4187   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4188                                                           polyhedron creation !!! */
4189   // Loop on elem nodes:
4190   // find new nodes and detect same nodes indices
4191   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4192   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4193   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4194   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4195
4196   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4197   vector<int> sames(nbNodes);
4198   vector<bool> isSingleNode(nbNodes);
4199
4200   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4202     const SMDS_MeshNode*                         node = nnIt->first;
4203     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4204     if ( listNewNodes.empty() )
4205       return;
4206
4207     itNN   [ iNode ] = listNewNodes.begin();
4208     prevNod[ iNode ] = node;
4209     nextNod[ iNode ] = listNewNodes.front();
4210
4211     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4212                                                              corner node of linear */
4213     if ( prevNod[ iNode ] != nextNod [ iNode ])
4214       nbDouble += !isSingleNode[iNode];
4215
4216     if( iNode < nbCorners ) { // check corners only
4217       if ( prevNod[ iNode ] == nextNod [ iNode ])
4218         sames[nbSame++] = iNode;
4219       else
4220         iNotSameNode = iNode;
4221     }
4222   }
4223
4224   if ( nbSame == nbNodes || nbSame > 2) {
4225     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4226     return;
4227   }
4228
4229   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4230   {
4231     // fix nodes order to have bottom normal external
4232     if ( baseType == SMDSEntity_Polygon )
4233     {
4234       std::reverse( itNN.begin(), itNN.end() );
4235       std::reverse( prevNod.begin(), prevNod.end() );
4236       std::reverse( midlNod.begin(), midlNod.end() );
4237       std::reverse( nextNod.begin(), nextNod.end() );
4238       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4239     }
4240     else
4241     {
4242       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4243       SMDS_MeshCell::applyInterlace( ind, itNN );
4244       SMDS_MeshCell::applyInterlace( ind, prevNod );
4245       SMDS_MeshCell::applyInterlace( ind, nextNod );
4246       SMDS_MeshCell::applyInterlace( ind, midlNod );
4247       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4248       if ( nbSame > 0 )
4249       {
4250         sames[nbSame] = iNotSameNode;
4251         for ( int j = 0; j <= nbSame; ++j )
4252           for ( size_t i = 0; i < ind.size(); ++i )
4253             if ( ind[i] == sames[j] )
4254             {
4255               sames[j] = i;
4256               break;
4257             }
4258         iNotSameNode = sames[nbSame];
4259       }
4260     }
4261   }
4262
4263   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4264   if ( nbSame > 0 ) {
4265     iSameNode    = sames[ nbSame-1 ];
4266     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4267     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4268     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4269   }
4270
4271   // make new elements
4272   for (int iStep = 0; iStep < nbSteps; iStep++ )
4273   {
4274     // get next nodes
4275     for ( iNode = 0; iNode < nbNodes; iNode++ )
4276     {
4277       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4278       nextNod[ iNode ] = *itNN[ iNode ]++;
4279     }
4280
4281     SMDS_MeshElement* aNewElem = 0;
4282     /*if(!elem->IsPoly())*/ {
4283       switch ( baseType ) {
4284       case SMDSEntity_0D:
4285       case SMDSEntity_Node: { // sweep NODE
4286         if ( nbSame == 0 ) {
4287           if ( isSingleNode[0] )
4288             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4289           else
4290             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4291         }
4292         else
4293           return;
4294         break;
4295       }
4296       case SMDSEntity_Edge: { // sweep EDGE
4297         if ( nbDouble == 0 )
4298         {
4299           if ( nbSame == 0 ) // ---> quadrangle
4300             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4301                                       nextNod[ 1 ], nextNod[ 0 ] );
4302           else               // ---> triangle
4303             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4304                                       nextNod[ iNotSameNode ] );
4305         }
4306         else                 // ---> polygon
4307         {
4308           vector<const SMDS_MeshNode*> poly_nodes;
4309           poly_nodes.push_back( prevNod[0] );
4310           poly_nodes.push_back( prevNod[1] );
4311           if ( prevNod[1] != nextNod[1] )
4312           {
4313             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4314             poly_nodes.push_back( nextNod[1] );
4315           }
4316           if ( prevNod[0] != nextNod[0] )
4317           {
4318             poly_nodes.push_back( nextNod[0] );
4319             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4320           }
4321           switch ( poly_nodes.size() ) {
4322           case 3:
4323             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4324             break;
4325           case 4:
4326             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4327                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4328             break;
4329           default:
4330             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4331           }
4332         }
4333         break;
4334       }
4335       case SMDSEntity_Triangle: // TRIANGLE --->
4336         {
4337           if ( nbDouble > 0 ) break;
4338           if ( nbSame == 0 )       // ---> pentahedron
4339             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4340                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4341
4342           else if ( nbSame == 1 )  // ---> pyramid
4343             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4344                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4345                                          nextNod[ iSameNode ]);
4346
4347           else // 2 same nodes:       ---> tetrahedron
4348             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4349                                          nextNod[ iNotSameNode ]);
4350           break;
4351         }
4352       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4353         {
4354           if ( nbSame == 2 )
4355             return;
4356           if ( nbDouble+nbSame == 2 )
4357           {
4358             if(nbSame==0) {      // ---> quadratic quadrangle
4359               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4360                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4361             }
4362             else { //(nbSame==1) // ---> quadratic triangle
4363               if(sames[0]==2) {
4364                 return; // medium node on axis
4365               }
4366               else if(sames[0]==0)
4367                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4368                                           nextNod[2], midlNod[1], prevNod[2]);
4369               else // sames[0]==1
4370                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4371                                           midlNod[0], nextNod[2], prevNod[2]);
4372             }
4373           }
4374           else if ( nbDouble == 3 )
4375           {
4376             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4377               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4378                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4379             }
4380           }
4381           else
4382             return;
4383           break;
4384         }
4385       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4386         if ( nbDouble > 0 ) break;
4387
4388         if ( nbSame == 0 )       // ---> hexahedron
4389           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4390                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4391
4392         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4393           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4394                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4395                                        nextNod[ iSameNode ]);
4396           newElems.push_back( aNewElem );
4397           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4398                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4399                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4400         }
4401         else if ( nbSame == 2 ) { // ---> pentahedron
4402           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4403             // iBeforeSame is same too
4404             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4405                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4406                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4407           else
4408             // iAfterSame is same too
4409             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4410                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4411                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4412         }
4413         break;
4414       }
4415       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4416       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4417         if ( nbDouble+nbSame != 3 ) break;
4418         if(nbSame==0) {
4419           // --->  pentahedron with 15 nodes
4420           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4421                                        nextNod[0], nextNod[1], nextNod[2],
4422                                        prevNod[3], prevNod[4], prevNod[5],
4423                                        nextNod[3], nextNod[4], nextNod[5],
4424                                        midlNod[0], midlNod[1], midlNod[2]);
4425         }
4426         else if(nbSame==1) {
4427           // --->  2d order pyramid of 13 nodes
4428           int apex = iSameNode;
4429           int i0 = ( apex + 1 ) % nbCorners;
4430           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4431           int i0a = apex + 3;
4432           int i1a = i1 + 3;
4433           int i01 = i0 + 3;
4434           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4435                                       nextNod[i0], nextNod[i1], prevNod[apex],
4436                                       prevNod[i01], midlNod[i0],
4437                                       nextNod[i01], midlNod[i1],
4438                                       prevNod[i1a], prevNod[i0a],
4439                                       nextNod[i0a], nextNod[i1a]);
4440         }
4441         else if(nbSame==2) {
4442           // --->  2d order tetrahedron of 10 nodes
4443           int n1 = iNotSameNode;
4444           int n2 = ( n1 + 1             ) % nbCorners;
4445           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4446           int n12 = n1 + 3;
4447           int n23 = n2 + 3;
4448           int n31 = n3 + 3;
4449           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4450                                        prevNod[n12], prevNod[n23], prevNod[n31],
4451                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4452         }
4453         break;
4454       }
4455       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4456         if( nbSame == 0 ) {
4457           if ( nbDouble != 4 ) break;
4458           // --->  hexahedron with 20 nodes
4459           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4460                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4461                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4462                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4463                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4464         }
4465         else if(nbSame==1) {
4466           // ---> pyramid + pentahedron - can not be created since it is needed
4467           // additional middle node at the center of face
4468           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4469           return;
4470         }
4471         else if( nbSame == 2 ) {
4472           if ( nbDouble != 2 ) break;
4473           // --->  2d order Pentahedron with 15 nodes
4474           int n1,n2,n4,n5;
4475           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4476             // iBeforeSame is same too
4477             n1 = iBeforeSame;
4478             n2 = iOpposSame;
4479             n4 = iSameNode;
4480             n5 = iAfterSame;
4481           }
4482           else {
4483             // iAfterSame is same too
4484             n1 = iSameNode;
4485             n2 = iBeforeSame;
4486             n4 = iAfterSame;
4487             n5 = iOpposSame;
4488           }
4489           int n12 = n2 + 4;
4490           int n45 = n4 + 4;
4491           int n14 = n1 + 4;
4492           int n25 = n5 + 4;
4493           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4494                                        prevNod[n4], prevNod[n5], nextNod[n5],
4495                                        prevNod[n12], midlNod[n2], nextNod[n12],
4496                                        prevNod[n45], midlNod[n5], nextNod[n45],
4497                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4498         }
4499         break;
4500       }
4501       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4502
4503         if( nbSame == 0 && nbDouble == 9 ) {
4504           // --->  tri-quadratic hexahedron with 27 nodes
4505           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4506                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4507                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4508                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4509                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4510                                        prevNod[8], // bottom center
4511                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4512                                        nextNod[8], // top center
4513                                        midlNod[8]);// elem center
4514         }
4515         else
4516         {
4517           return;
4518         }
4519         break;
4520       }
4521       case SMDSEntity_Polygon: { // sweep POLYGON
4522
4523         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4524           // --->  hexagonal prism
4525           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4526                                        prevNod[3], prevNod[4], prevNod[5],
4527                                        nextNod[0], nextNod[1], nextNod[2],
4528                                        nextNod[3], nextNod[4], nextNod[5]);
4529         }
4530         break;
4531       }
4532       case SMDSEntity_Ball:
4533         return;
4534
4535       default:
4536         break;
4537       }
4538     }
4539
4540     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4541     {
4542       if ( baseType != SMDSEntity_Polygon )
4543       {
4544         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4545         SMDS_MeshCell::applyInterlace( ind, prevNod );
4546         SMDS_MeshCell::applyInterlace( ind, nextNod );
4547         SMDS_MeshCell::applyInterlace( ind, midlNod );
4548         SMDS_MeshCell::applyInterlace( ind, itNN );
4549         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4550         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4551       }
4552       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4553       vector<int> quantities (nbNodes + 2);
4554       polyedre_nodes.clear();
4555       quantities.clear();
4556
4557       // bottom of prism
4558       for (int inode = 0; inode < nbNodes; inode++)
4559         polyedre_nodes.push_back( prevNod[inode] );
4560       quantities.push_back( nbNodes );
4561
4562       // top of prism
4563       polyedre_nodes.push_back( nextNod[0] );
4564       for (int inode = nbNodes; inode-1; --inode )
4565         polyedre_nodes.push_back( nextNod[inode-1] );
4566       quantities.push_back( nbNodes );
4567
4568       // side faces
4569       for (int iface = 0; iface < nbNodes; iface++)
4570       {
4571         const int prevNbNodes = polyedre_nodes.size();
4572         int inextface = (iface+1) % nbNodes;
4573         polyedre_nodes.push_back( prevNod[inextface] );
4574         polyedre_nodes.push_back( prevNod[iface] );
4575         if ( prevNod[iface] != nextNod[iface] )
4576         {
4577           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4578           polyedre_nodes.push_back( nextNod[iface] );
4579         }
4580         if ( prevNod[inextface] != nextNod[inextface] )
4581         {
4582           polyedre_nodes.push_back( nextNod[inextface] );
4583           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4584         }
4585         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4586         if ( nbFaceNodes > 2 )
4587           quantities.push_back( nbFaceNodes );
4588         else // degenerated face
4589           polyedre_nodes.resize( prevNbNodes );
4590       }
4591       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4592     }
4593
4594     if ( aNewElem ) {
4595       newElems.push_back( aNewElem );
4596       myLastCreatedElems.Append(aNewElem);
4597       srcElements.Append( elem );
4598     }
4599
4600     // set new prev nodes
4601     for ( iNode = 0; iNode < nbNodes; iNode++ )
4602       prevNod[ iNode ] = nextNod[ iNode ];
4603
4604   } // for steps
4605 }
4606
4607 //=======================================================================
4608 /*!
4609  * \brief Create 1D and 2D elements around swept elements
4610  * \param mapNewNodes - source nodes and ones generated from them
4611  * \param newElemsMap - source elements and ones generated from them
4612  * \param elemNewNodesMap - nodes generated from each node of each element
4613  * \param elemSet - all swept elements
4614  * \param nbSteps - number of sweeping steps
4615  * \param srcElements - to append elem for each generated element
4616  */
4617 //=======================================================================
4618
4619 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4620                                   TTElemOfElemListMap &    newElemsMap,
4621                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4622                                   TIDSortedElemSet&        elemSet,
4623                                   const int                nbSteps,
4624                                   SMESH_SequenceOfElemPtr& srcElements)
4625 {
4626   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4627   SMESHDS_Mesh* aMesh = GetMeshDS();
4628
4629   // Find nodes belonging to only one initial element - sweep them into edges.
4630
4631   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4632   for ( ; nList != mapNewNodes.end(); nList++ )
4633   {
4634     const SMDS_MeshNode* node =
4635       static_cast<const SMDS_MeshNode*>( nList->first );
4636     if ( newElemsMap.count( node ))
4637       continue; // node was extruded into edge
4638     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4639     int nbInitElems = 0;
4640     const SMDS_MeshElement* el = 0;
4641     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4642     while ( eIt->more() && nbInitElems < 2 ) {
4643       el = eIt->next();
4644       SMDSAbs_ElementType type = el->GetType();
4645       if ( type == SMDSAbs_Volume || type < highType ) continue;
4646       if ( type > highType ) {
4647         nbInitElems = 0;
4648         highType = type;
4649       }
4650       nbInitElems += elemSet.count(el);
4651     }
4652     if ( nbInitElems < 2 ) {
4653       bool NotCreateEdge = el && el->IsMediumNode(node);
4654       if(!NotCreateEdge) {
4655         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4656         list<const SMDS_MeshElement*> newEdges;
4657         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4658       }
4659     }
4660   }
4661
4662   // Make a ceiling for each element ie an equal element of last new nodes.
4663   // Find free links of faces - make edges and sweep them into faces.
4664
4665   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4666   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4667   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4668   {
4669     const SMDS_MeshElement* elem = itElem->first;
4670     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4671
4672     if(itElem->second.size()==0) continue;
4673
4674     const bool isQuadratic = elem->IsQuadratic();
4675
4676     if ( elem->GetType() == SMDSAbs_Edge ) {
4677       // create a ceiling edge
4678       if ( !isQuadratic ) {
4679         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4680                                vecNewNodes[ 1 ]->second.back())) {
4681           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4682                                                    vecNewNodes[ 1 ]->second.back()));
4683           srcElements.Append( elem );
4684         }
4685       }
4686       else {
4687         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4688                                vecNewNodes[ 1 ]->second.back(),
4689                                vecNewNodes[ 2 ]->second.back())) {
4690           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4691                                                    vecNewNodes[ 1 ]->second.back(),
4692                                                    vecNewNodes[ 2 ]->second.back()));
4693           srcElements.Append( elem );
4694         }
4695       }
4696     }
4697     if ( elem->GetType() != SMDSAbs_Face )
4698       continue;
4699
4700     bool hasFreeLinks = false;
4701
4702     TIDSortedElemSet avoidSet;
4703     avoidSet.insert( elem );
4704
4705     set<const SMDS_MeshNode*> aFaceLastNodes;
4706     int iNode, nbNodes = vecNewNodes.size();
4707     if ( !isQuadratic ) {
4708       // loop on the face nodes
4709       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4710         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4711         // look for free links of the face
4712         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4713         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4714         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4715         // check if a link n1-n2 is free
4716         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4717           hasFreeLinks = true;
4718           // make a new edge and a ceiling for a new edge
4719           const SMDS_MeshElement* edge;
4720           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4721             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4722             srcElements.Append( myLastCreatedElems.Last() );
4723           }
4724           n1 = vecNewNodes[ iNode ]->second.back();
4725           n2 = vecNewNodes[ iNext ]->second.back();
4726           if ( !aMesh->FindEdge( n1, n2 )) {
4727             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4728             srcElements.Append( edge );
4729           }
4730         }
4731       }
4732     }
4733     else { // elem is quadratic face
4734       int nbn = nbNodes/2;
4735       for ( iNode = 0; iNode < nbn; iNode++ ) {
4736         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4737         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4738         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4739         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4740         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4741         // check if a link is free
4742         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4743              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4744              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4745           hasFreeLinks = true;
4746           // make an edge and a ceiling for a new edge
4747           // find medium node
4748           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4749             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4750             srcElements.Append( elem );
4751           }
4752           n1 = vecNewNodes[ iNode ]->second.back();
4753           n2 = vecNewNodes[ iNext ]->second.back();
4754           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4755           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4756             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4757             srcElements.Append( elem );
4758           }
4759         }
4760       }
4761       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4762         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4763       }
4764     }
4765
4766     // sweep free links into faces
4767
4768     if ( hasFreeLinks )  {
4769       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4770       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4771
4772       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4773       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4774       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4775         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4776         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4777       }
4778       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4779         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4780         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4781       }
4782       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4783         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4784         std::advance( v, volNb );
4785         // find indices of free faces of a volume and their source edges
4786         list< int > freeInd;
4787         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4788         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4789         int iF, nbF = vTool.NbFaces();
4790         for ( iF = 0; iF < nbF; iF ++ ) {
4791           if (vTool.IsFreeFace( iF ) &&
4792               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4793               initNodeSet != faceNodeSet) // except an initial face
4794           {
4795             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4796               continue;
4797             if ( faceNodeSet == initNodeSetNoCenter )
4798               continue;
4799             freeInd.push_back( iF );
4800             // find source edge of a free face iF
4801             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4802             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4803             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4804                                    initNodeSet.begin(), initNodeSet.end(),
4805                                    commonNodes.begin());
4806             if ( (*v)->IsQuadratic() )
4807               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4808             else
4809               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4810 #ifdef _DEBUG_
4811             if ( !srcEdges.back() )
4812             {
4813               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4814                    << iF << " of volume #" << vTool.ID() << endl;
4815             }
4816 #endif
4817           }
4818         }
4819         if ( freeInd.empty() )
4820           continue;
4821
4822         // create faces for all steps;
4823         // if such a face has been already created by sweep of edge,
4824         // assure that its orientation is OK
4825         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4826           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4827           vTool.SetExternalNormal();
4828           const int nextShift = vTool.IsForward() ? +1 : -1;
4829           list< int >::iterator ind = freeInd.begin();
4830           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4831           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4832           {
4833             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4834             int nbn = vTool.NbFaceNodes( *ind );
4835             const SMDS_MeshElement * f = 0;
4836             if ( nbn == 3 )              ///// triangle
4837             {
4838               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4839               if ( !f ||
4840                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4841               {
4842                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4843                                                      nodes[ 1 ],
4844                                                      nodes[ 1 + nextShift ] };
4845                 if ( f )
4846                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4847                 else
4848                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4849                                                             newOrder[ 2 ] ));
4850               }
4851             }
4852             else if ( nbn == 4 )       ///// quadrangle
4853             {
4854               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4855               if ( !f ||
4856                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4857               {
4858                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4859                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4860                 if ( f )
4861                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4862                 else
4863                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4864                                                             newOrder[ 2 ], newOrder[ 3 ]));
4865               }
4866             }
4867             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4868             {
4869               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4870               if ( !f ||
4871                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4872               {
4873                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4874                                                      nodes[2],
4875                                                      nodes[2 + 2*nextShift],
4876                                                      nodes[3 - 2*nextShift],
4877                                                      nodes[3],
4878                                                      nodes[3 + 2*nextShift]};
4879                 if ( f )
4880                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4881                 else
4882                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4883                                                             newOrder[ 1 ],
4884                                                             newOrder[ 2 ],
4885                                                             newOrder[ 3 ],
4886                                                             newOrder[ 4 ],
4887                                                             newOrder[ 5 ] ));
4888               }
4889             }
4890             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4891             {
4892               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4893                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4894               if ( !f ||
4895                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4896               {
4897                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4898                                                      nodes[4 - 2*nextShift],
4899                                                      nodes[4],
4900                                                      nodes[4 + 2*nextShift],
4901                                                      nodes[1],
4902                                                      nodes[5 - 2*nextShift],
4903                                                      nodes[5],
4904                                                      nodes[5 + 2*nextShift] };
4905                 if ( f )
4906                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4907                 else
4908                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4909                                                            newOrder[ 2 ], newOrder[ 3 ],
4910                                                            newOrder[ 4 ], newOrder[ 5 ],
4911                                                            newOrder[ 6 ], newOrder[ 7 ]));
4912               }
4913             }
4914             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4915             {
4916               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4917                                       SMDSAbs_Face, /*noMedium=*/false);
4918               if ( !f ||
4919                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4920               {
4921                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4922                                                      nodes[4 - 2*nextShift],
4923                                                      nodes[4],
4924                                                      nodes[4 + 2*nextShift],
4925                                                      nodes[1],
4926                                                      nodes[5 - 2*nextShift],
4927                                                      nodes[5],
4928                                                      nodes[5 + 2*nextShift],
4929                                                      nodes[8] };
4930                 if ( f )
4931                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4932                 else
4933                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4934                                                            newOrder[ 2 ], newOrder[ 3 ],
4935                                                            newOrder[ 4 ], newOrder[ 5 ],
4936                                                            newOrder[ 6 ], newOrder[ 7 ],
4937                                                            newOrder[ 8 ]));
4938               }
4939             }
4940             else  //////// polygon
4941             {
4942               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4943               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4944               if ( !f ||
4945                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4946               {
4947                 if ( !vTool.IsForward() )
4948                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4949                 if ( f )
4950                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4951                 else
4952                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4953               }
4954             }
4955
4956             while ( srcElements.Length() < myLastCreatedElems.Length() )
4957               srcElements.Append( *srcEdge );
4958
4959           }  // loop on free faces
4960
4961           // go to the next volume
4962           iVol = 0;
4963           while ( iVol++ < nbVolumesByStep ) v++;
4964
4965         } // loop on steps
4966       } // loop on volumes of one step
4967     } // sweep free links into faces
4968
4969     // Make a ceiling face with a normal external to a volume
4970
4971     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4972     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4973     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4974
4975     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4976       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4977       iF = lastVol.GetFaceIndex( aFaceLastNodes );
4978     }
4979     if ( iF >= 0 ) {
4980       lastVol.SetExternalNormal();
4981       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4982       int nbn = lastVol.NbFaceNodes( iF );
4983       // we do not use this->AddElement() because nodes are interlaced
4984       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4985       if ( !hasFreeLinks ||
4986            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4987       {
4988         if ( nbn == 3 )
4989           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4990
4991         else if ( nbn == 4 )
4992           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4993
4994         else if ( nbn == 6 && isQuadratic )
4995           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4996                                                     nodes[1], nodes[3], nodes[5]));
4997         else if ( nbn == 7 && isQuadratic )
4998           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4999                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
5000         else if ( nbn == 8 && isQuadratic )
5001           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5002                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5003         else if ( nbn == 9 && isQuadratic )
5004           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5005                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5006                                                     nodes[8]));
5007         else
5008           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5009
5010         while ( srcElements.Length() < myLastCreatedElems.Length() )
5011           srcElements.Append( elem );
5012       }
5013     }
5014   } // loop on swept elements
5015 }
5016
5017 //=======================================================================
5018 //function : RotationSweep
5019 //purpose  :
5020 //=======================================================================
5021
5022 SMESH_MeshEditor::PGroupIDs
5023 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5024                                 const gp_Ax1&      theAxis,
5025                                 const double       theAngle,
5026                                 const int          theNbSteps,
5027                                 const double       theTol,
5028                                 const bool         theMakeGroups,
5029                                 const bool         theMakeWalls)
5030 {
5031   myLastCreatedElems.Clear();
5032   myLastCreatedNodes.Clear();
5033
5034   // source elements for each generated one
5035   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5036
5037   MESSAGE( "RotationSweep()");
5038   gp_Trsf aTrsf;
5039   aTrsf.SetRotation( theAxis, theAngle );
5040   gp_Trsf aTrsf2;
5041   aTrsf2.SetRotation( theAxis, theAngle/2. );
5042
5043   gp_Lin aLine( theAxis );
5044   double aSqTol = theTol * theTol;
5045
5046   SMESHDS_Mesh* aMesh = GetMeshDS();
5047
5048   TNodeOfNodeListMap mapNewNodes;
5049   TElemOfVecOfNnlmiMap mapElemNewNodes;
5050   TTElemOfElemListMap newElemsMap;
5051
5052   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5053                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5054                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5055   // loop on theElems
5056   TIDSortedElemSet::iterator itElem;
5057   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5058     const SMDS_MeshElement* elem = *itElem;
5059     if ( !elem || elem->GetType() == SMDSAbs_Volume )
5060       continue;
5061     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5062     newNodesItVec.reserve( elem->NbNodes() );
5063
5064     // loop on elem nodes
5065     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5066     while ( itN->more() )
5067     {
5068       // check if a node has been already sweeped
5069       const SMDS_MeshNode* node = cast2Node( itN->next() );
5070
5071       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5072       double coord[3];
5073       aXYZ.Coord( coord[0], coord[1], coord[2] );
5074       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5075
5076       TNodeOfNodeListMapItr nIt =
5077         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5078       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5079       if ( listNewNodes.empty() )
5080       {
5081         // check if we are to create medium nodes between corner ones
5082         bool needMediumNodes = false;
5083         if ( isQuadraticMesh )
5084         {
5085           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5086           while (it->more() && !needMediumNodes )
5087           {
5088             const SMDS_MeshElement* invElem = it->next();
5089             if ( invElem != elem && !theElems.count( invElem )) continue;
5090             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5091             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5092               needMediumNodes = true;
5093           }
5094         }
5095
5096         // make new nodes
5097         const SMDS_MeshNode * newNode = node;
5098         for ( int i = 0; i < theNbSteps; i++ ) {
5099           if ( !isOnAxis ) {
5100             if ( needMediumNodes )  // create a medium node
5101             {
5102               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5103               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5104               myLastCreatedNodes.Append(newNode);
5105               srcNodes.Append( node );
5106               listNewNodes.push_back( newNode );
5107               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5108             }
5109             else {
5110               aTrsf.Transforms( coord[0], coord[1], coord[2] );
5111             }
5112             // create a corner node
5113             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5114             myLastCreatedNodes.Append(newNode);
5115             srcNodes.Append( node );
5116             listNewNodes.push_back( newNode );
5117           }
5118           else {
5119             listNewNodes.push_back( newNode );
5120             // if ( needMediumNodes )
5121             //   listNewNodes.push_back( newNode );
5122           }
5123         }
5124       }
5125       newNodesItVec.push_back( nIt );
5126     }
5127     // make new elements
5128     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5129   }
5130
5131   if ( theMakeWalls )
5132     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5133
5134   PGroupIDs newGroupIDs;
5135   if ( theMakeGroups )
5136     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5137
5138   return newGroupIDs;
5139 }
5140
5141
5142 //=======================================================================
5143 //function : CreateNode
5144 //purpose  :
5145 //=======================================================================
5146 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5147                                                   const double y,
5148                                                   const double z,
5149                                                   const double tolnode,
5150                                                   SMESH_SequenceOfNode& aNodes)
5151 {
5152   // myLastCreatedElems.Clear();
5153   // myLastCreatedNodes.Clear();
5154
5155   gp_Pnt P1(x,y,z);
5156   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5157
5158   // try to search in sequence of existing nodes
5159   // if aNodes.Length()>0 we 'nave to use given sequence
5160   // else - use all nodes of mesh
5161   if(aNodes.Length()>0) {
5162     int i;
5163     for(i=1; i<=aNodes.Length(); i++) {
5164       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5165       if(P1.Distance(P2)<tolnode)
5166         return aNodes.Value(i);
5167     }
5168   }
5169   else {
5170     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5171     while(itn->more()) {
5172       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5173       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5174       if(P1.Distance(P2)<tolnode)
5175         return aN;
5176     }
5177   }
5178
5179   // create new node and return it
5180   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5181   //myLastCreatedNodes.Append(NewNode);
5182   return NewNode;
5183 }
5184
5185
5186 //=======================================================================
5187 //function : ExtrusionSweep
5188 //purpose  :
5189 //=======================================================================
5190
5191 SMESH_MeshEditor::PGroupIDs
5192 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5193                                   const gp_Vec&        theStep,
5194                                   const int            theNbSteps,
5195                                   TTElemOfElemListMap& newElemsMap,
5196                                   const bool           theMakeGroups,
5197                                   const int            theFlags,
5198                                   const double         theTolerance)
5199 {
5200   ExtrusParam aParams;
5201   aParams.myDir = gp_Dir(theStep);
5202   aParams.myNodes.Clear();
5203   aParams.mySteps = new TColStd_HSequenceOfReal;
5204   int i;
5205   for(i=1; i<=theNbSteps; i++)
5206     aParams.mySteps->Append(theStep.Magnitude());
5207
5208   return
5209     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5210 }
5211
5212
5213 //=======================================================================
5214 //function : ExtrusionSweep
5215 //purpose  :
5216 //=======================================================================
5217
5218 SMESH_MeshEditor::PGroupIDs
5219 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5220                                   ExtrusParam&         theParams,
5221                                   TTElemOfElemListMap& newElemsMap,
5222                                   const bool           theMakeGroups,
5223                                   const int            theFlags,
5224                                   const double         theTolerance)
5225 {
5226   myLastCreatedElems.Clear();
5227   myLastCreatedNodes.Clear();
5228
5229   // source elements for each generated one
5230   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5231
5232   SMESHDS_Mesh* aMesh = GetMeshDS();
5233
5234   int nbsteps = theParams.mySteps->Length();
5235
5236   TNodeOfNodeListMap mapNewNodes;
5237   //TNodeOfNodeVecMap mapNewNodes;
5238   TElemOfVecOfNnlmiMap mapElemNewNodes;
5239   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5240
5241   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5242                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5243                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5244   // loop on theElems
5245   TIDSortedElemSet::iterator itElem;
5246   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5247     // check element type
5248     const SMDS_MeshElement* elem = *itElem;
5249     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5250       continue;
5251
5252     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5253     newNodesItVec.reserve( elem->NbNodes() );
5254
5255     // loop on elem nodes
5256     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5257     while ( itN->more() )
5258     {
5259       // check if a node has been already sweeped
5260       const SMDS_MeshNode* node = cast2Node( itN->next() );
5261       TNodeOfNodeListMap::iterator nIt =
5262         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5263       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5264       if ( listNewNodes.empty() )
5265       {
5266         // make new nodes
5267
5268         // check if we are to create medium nodes between corner ones
5269         bool needMediumNodes = false;
5270         if ( isQuadraticMesh )
5271         {
5272           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5273           while (it->more() && !needMediumNodes )
5274           {
5275             const SMDS_MeshElement* invElem = it->next();
5276             if ( invElem != elem && !theElems.count( invElem )) continue;
5277             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5278             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5279               needMediumNodes = true;
5280           }
5281         }
5282
5283         double coord[] = { node->X(), node->Y(), node->Z() };
5284         for ( int i = 0; i < nbsteps; i++ )
5285         {
5286           if ( needMediumNodes ) // create a medium node
5287           {
5288             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5289             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5290             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5291             if( theFlags & EXTRUSION_FLAG_SEW ) {
5292               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5293                                                          theTolerance, theParams.myNodes);
5294               listNewNodes.push_back( newNode );
5295             }
5296             else {
5297               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5298               myLastCreatedNodes.Append(newNode);
5299               srcNodes.Append( node );
5300               listNewNodes.push_back( newNode );
5301             }
5302           }
5303           // create a corner node
5304           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5305           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5306           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5307           if( theFlags & EXTRUSION_FLAG_SEW ) {
5308             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5309                                                        theTolerance, theParams.myNodes);
5310             listNewNodes.push_back( newNode );
5311           }
5312           else {
5313             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5314             myLastCreatedNodes.Append(newNode);
5315             srcNodes.Append( node );
5316             listNewNodes.push_back( newNode );
5317           }
5318         }
5319       }
5320       newNodesItVec.push_back( nIt );
5321     }
5322     // make new elements
5323     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5324   }
5325
5326   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5327     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5328   }
5329   PGroupIDs newGroupIDs;
5330   if ( theMakeGroups )
5331     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5332
5333   return newGroupIDs;
5334 }
5335
5336 //=======================================================================
5337 //function : ExtrusionAlongTrack
5338 //purpose  :
5339 //=======================================================================
5340 SMESH_MeshEditor::Extrusion_Error
5341 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5342                                        SMESH_subMesh*       theTrack,
5343                                        const SMDS_MeshNode* theN1,
5344                                        const bool           theHasAngles,
5345                                        list<double>&        theAngles,
5346                                        const bool           theLinearVariation,
5347                                        const bool           theHasRefPoint,
5348                                        const gp_Pnt&        theRefPoint,
5349                                        const bool           theMakeGroups)
5350 {
5351   MESSAGE("ExtrusionAlongTrack");
5352   myLastCreatedElems.Clear();
5353   myLastCreatedNodes.Clear();
5354
5355   int aNbE;
5356   std::list<double> aPrms;
5357   TIDSortedElemSet::iterator itElem;
5358
5359   gp_XYZ aGC;
5360   TopoDS_Edge aTrackEdge;
5361   TopoDS_Vertex aV1, aV2;
5362
5363   SMDS_ElemIteratorPtr aItE;
5364   SMDS_NodeIteratorPtr aItN;
5365   SMDSAbs_ElementType aTypeE;
5366
5367   TNodeOfNodeListMap mapNewNodes;
5368
5369   // 1. Check data
5370   aNbE = theElements.size();
5371   // nothing to do
5372   if ( !aNbE )
5373     return EXTR_NO_ELEMENTS;
5374
5375   // 1.1 Track Pattern
5376   ASSERT( theTrack );
5377
5378   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5379
5380   aItE = pSubMeshDS->GetElements();
5381   while ( aItE->more() ) {
5382     const SMDS_MeshElement* pE = aItE->next();
5383     aTypeE = pE->GetType();
5384     // Pattern must contain links only
5385     if ( aTypeE != SMDSAbs_Edge )
5386       return EXTR_PATH_NOT_EDGE;
5387   }
5388
5389   list<SMESH_MeshEditor_PathPoint> fullList;
5390
5391   const TopoDS_Shape& aS = theTrack->GetSubShape();
5392   // Sub-shape for the Pattern must be an Edge or Wire
5393   if( aS.ShapeType() == TopAbs_EDGE ) {
5394     aTrackEdge = TopoDS::Edge( aS );
5395     // the Edge must not be degenerated
5396     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5397       return EXTR_BAD_PATH_SHAPE;
5398     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5399     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5400     const SMDS_MeshNode* aN1 = aItN->next();
5401     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5402     const SMDS_MeshNode* aN2 = aItN->next();
5403     // starting node must be aN1 or aN2
5404     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5405       return EXTR_BAD_STARTING_NODE;
5406     aItN = pSubMeshDS->GetNodes();
5407     while ( aItN->more() ) {
5408       const SMDS_MeshNode* pNode = aItN->next();
5409       const SMDS_EdgePosition* pEPos =
5410         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5411       double aT = pEPos->GetUParameter();
5412       aPrms.push_back( aT );
5413     }
5414     //Extrusion_Error err =
5415     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5416   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5417     list< SMESH_subMesh* > LSM;
5418     TopTools_SequenceOfShape Edges;
5419     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5420     while(itSM->more()) {
5421       SMESH_subMesh* SM = itSM->next();
5422       LSM.push_back(SM);
5423       const TopoDS_Shape& aS = SM->GetSubShape();
5424       Edges.Append(aS);
5425     }
5426     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5427     int startNid = theN1->GetID();
5428     TColStd_MapOfInteger UsedNums;
5429
5430     int NbEdges = Edges.Length();
5431     int i = 1;
5432     for(; i<=NbEdges; i++) {
5433       int k = 0;
5434       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5435       for(; itLSM!=LSM.end(); itLSM++) {
5436         k++;
5437         if(UsedNums.Contains(k)) continue;
5438         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5439         SMESH_subMesh* locTrack = *itLSM;
5440         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5441         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5442         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5443         const SMDS_MeshNode* aN1 = aItN->next();
5444         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5445         const SMDS_MeshNode* aN2 = aItN->next();
5446         // starting node must be aN1 or aN2
5447         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5448         // 2. Collect parameters on the track edge
5449         aPrms.clear();
5450         aItN = locMeshDS->GetNodes();
5451         while ( aItN->more() ) {
5452           const SMDS_MeshNode* pNode = aItN->next();
5453           const SMDS_EdgePosition* pEPos =
5454             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5455           double aT = pEPos->GetUParameter();
5456           aPrms.push_back( aT );
5457         }
5458         list<SMESH_MeshEditor_PathPoint> LPP;
5459         //Extrusion_Error err =
5460         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5461         LLPPs.push_back(LPP);
5462         UsedNums.Add(k);
5463         // update startN for search following egde
5464         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5465         else startNid = aN1->GetID();
5466         break;
5467       }
5468     }
5469     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5470     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5471     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5472     for(; itPP!=firstList.end(); itPP++) {
5473       fullList.push_back( *itPP );
5474     }
5475     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5476     fullList.pop_back();
5477     itLLPP++;
5478     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5479       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5480       itPP = currList.begin();
5481       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5482       gp_Dir D1 = PP1.Tangent();
5483       gp_Dir D2 = PP2.Tangent();
5484       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5485                            (D1.Z()+D2.Z())/2 ) );
5486       PP1.SetTangent(Dnew);
5487       fullList.push_back(PP1);
5488       itPP++;
5489       for(; itPP!=firstList.end(); itPP++) {
5490         fullList.push_back( *itPP );
5491       }
5492       PP1 = fullList.back();
5493       fullList.pop_back();
5494     }
5495     // if wire not closed
5496     fullList.push_back(PP1);
5497     // else ???
5498   }
5499   else {
5500     return EXTR_BAD_PATH_SHAPE;
5501   }
5502
5503   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5504                           theHasRefPoint, theRefPoint, theMakeGroups);
5505 }
5506
5507
5508 //=======================================================================
5509 //function : ExtrusionAlongTrack
5510 //purpose  :
5511 //=======================================================================
5512 SMESH_MeshEditor::Extrusion_Error
5513 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5514                                        SMESH_Mesh*          theTrack,
5515                                        const SMDS_MeshNode* theN1,
5516                                        const bool           theHasAngles,
5517                                        list<double>&        theAngles,
5518                                        const bool           theLinearVariation,
5519                                        const bool           theHasRefPoint,
5520                                        const gp_Pnt&        theRefPoint,
5521                                        const bool           theMakeGroups)
5522 {
5523   myLastCreatedElems.Clear();
5524   myLastCreatedNodes.Clear();
5525
5526   int aNbE;
5527   std::list<double> aPrms;
5528   TIDSortedElemSet::iterator itElem;
5529
5530   gp_XYZ aGC;
5531   TopoDS_Edge aTrackEdge;
5532   TopoDS_Vertex aV1, aV2;
5533
5534   SMDS_ElemIteratorPtr aItE;
5535   SMDS_NodeIteratorPtr aItN;
5536   SMDSAbs_ElementType aTypeE;
5537
5538   TNodeOfNodeListMap mapNewNodes;
5539
5540   // 1. Check data
5541   aNbE = theElements.size();
5542   // nothing to do
5543   if ( !aNbE )
5544     return EXTR_NO_ELEMENTS;
5545
5546   // 1.1 Track Pattern
5547   ASSERT( theTrack );
5548
5549   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5550
5551   aItE = pMeshDS->elementsIterator();
5552   while ( aItE->more() ) {
5553     const SMDS_MeshElement* pE = aItE->next();
5554     aTypeE = pE->GetType();
5555     // Pattern must contain links only
5556     if ( aTypeE != SMDSAbs_Edge )
5557       return EXTR_PATH_NOT_EDGE;
5558   }
5559
5560   list<SMESH_MeshEditor_PathPoint> fullList;
5561
5562   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5563
5564   if ( !theTrack->HasShapeToMesh() ) {
5565     //Mesh without shape
5566     const SMDS_MeshNode* currentNode = NULL;
5567     const SMDS_MeshNode* prevNode = theN1;
5568     std::vector<const SMDS_MeshNode*> aNodesList;
5569     aNodesList.push_back(theN1);
5570     int nbEdges = 0, conn=0;
5571     const SMDS_MeshElement* prevElem = NULL;
5572     const SMDS_MeshElement* currentElem = NULL;
5573     int totalNbEdges = theTrack->NbEdges();
5574     SMDS_ElemIteratorPtr nIt;
5575
5576     //check start node
5577     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5578       return EXTR_BAD_STARTING_NODE;
5579     }
5580
5581     conn = nbEdgeConnectivity(theN1);
5582     if(conn > 2)
5583       return EXTR_PATH_NOT_EDGE;
5584
5585     aItE = theN1->GetInverseElementIterator();
5586     prevElem = aItE->next();
5587     currentElem = prevElem;
5588     //Get all nodes
5589     if(totalNbEdges == 1 ) {
5590       nIt = currentElem->nodesIterator();
5591       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5592       if(currentNode == prevNode)
5593         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5594       aNodesList.push_back(currentNode);
5595     } else {
5596       nIt = currentElem->nodesIterator();
5597       while( nIt->more() ) {
5598         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5599         if(currentNode == prevNode)
5600           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5601         aNodesList.push_back(currentNode);
5602
5603         //case of the closed mesh
5604         if(currentNode == theN1) {
5605           nbEdges++;
5606           break;
5607         }
5608
5609         conn = nbEdgeConnectivity(currentNode);
5610         if(conn > 2) {
5611           return EXTR_PATH_NOT_EDGE;
5612         }else if( conn == 1 && nbEdges > 0 ) {
5613           //End of the path
5614           nbEdges++;
5615           break;
5616         }else {
5617           prevNode = currentNode;
5618           aItE = currentNode->GetInverseElementIterator();
5619           currentElem = aItE->next();
5620           if( currentElem  == prevElem)
5621             currentElem = aItE->next();
5622           nIt = currentElem->nodesIterator();
5623           prevElem = currentElem;
5624           nbEdges++;
5625         }
5626       }
5627     }
5628
5629     if(nbEdges != totalNbEdges)
5630       return EXTR_PATH_NOT_EDGE;
5631
5632     TopTools_SequenceOfShape Edges;
5633     double x1,x2,y1,y2,z1,z2;
5634     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5635     int startNid = theN1->GetID();
5636     for(int i = 1; i < aNodesList.size(); i++) {
5637       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5638       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5639       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5640       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5641       list<SMESH_MeshEditor_PathPoint> LPP;
5642       aPrms.clear();
5643       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5644       LLPPs.push_back(LPP);
5645       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5646       else startNid = aNodesList[i-1]->GetID();
5647
5648     }
5649
5650     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5651     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5652     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5653     for(; itPP!=firstList.end(); itPP++) {
5654       fullList.push_back( *itPP );
5655     }
5656
5657     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5658     SMESH_MeshEditor_PathPoint PP2;
5659     fullList.pop_back();
5660     itLLPP++;
5661     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5662       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5663       itPP = currList.begin();
5664       PP2 = currList.front();
5665       gp_Dir D1 = PP1.Tangent();
5666       gp_Dir D2 = PP2.Tangent();
5667       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5668                            (D1.Z()+D2.Z())/2 ) );
5669       PP1.SetTangent(Dnew);
5670       fullList.push_back(PP1);
5671       itPP++;
5672       for(; itPP!=currList.end(); itPP++) {
5673         fullList.push_back( *itPP );
5674       }
5675       PP1 = fullList.back();
5676       fullList.pop_back();
5677     }
5678     fullList.push_back(PP1);
5679
5680   } // Sub-shape for the Pattern must be an Edge or Wire
5681   else if( aS.ShapeType() == TopAbs_EDGE ) {
5682     aTrackEdge = TopoDS::Edge( aS );
5683     // the Edge must not be degenerated
5684     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5685       return EXTR_BAD_PATH_SHAPE;
5686     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5687     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5688     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5689     // starting node must be aN1 or aN2
5690     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5691       return EXTR_BAD_STARTING_NODE;
5692     aItN = pMeshDS->nodesIterator();
5693     while ( aItN->more() ) {
5694       const SMDS_MeshNode* pNode = aItN->next();
5695       if( pNode==aN1 || pNode==aN2 ) continue;
5696       const SMDS_EdgePosition* pEPos =
5697         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5698       double aT = pEPos->GetUParameter();
5699       aPrms.push_back( aT );
5700     }
5701     //Extrusion_Error err =
5702     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5703   }
5704   else if( aS.ShapeType() == TopAbs_WIRE ) {
5705     list< SMESH_subMesh* > LSM;
5706     TopTools_SequenceOfShape Edges;
5707     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5708     for(; eExp.More(); eExp.Next()) {
5709       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5710       if( SMESH_Algo::isDegenerated(E) ) continue;
5711       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5712       if(SM) {
5713         LSM.push_back(SM);
5714         Edges.Append(E);
5715       }
5716     }
5717     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5718     TopoDS_Vertex aVprev;
5719     TColStd_MapOfInteger UsedNums;
5720     int NbEdges = Edges.Length();
5721     int i = 1;
5722     for(; i<=NbEdges; i++) {
5723       int k = 0;
5724       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5725       for(; itLSM!=LSM.end(); itLSM++) {
5726         k++;
5727         if(UsedNums.Contains(k)) continue;
5728         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5729         SMESH_subMesh* locTrack = *itLSM;
5730         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5731         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5732         bool aN1isOK = false, aN2isOK = false;
5733         if ( aVprev.IsNull() ) {
5734           // if previous vertex is not yet defined, it means that we in the beginning of wire
5735           // and we have to find initial vertex corresponding to starting node theN1
5736           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5737           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5738           // starting node must be aN1 or aN2
5739           aN1isOK = ( aN1 && aN1 == theN1 );
5740           aN2isOK = ( aN2 && aN2 == theN1 );
5741         }
5742         else {
5743           // we have specified ending vertex of the previous edge on the previous iteration
5744           // and we have just to check that it corresponds to any vertex in current segment
5745           aN1isOK = aVprev.IsSame( aV1 );
5746           aN2isOK = aVprev.IsSame( aV2 );
5747         }
5748         if ( !aN1isOK && !aN2isOK ) continue;
5749         // 2. Collect parameters on the track edge
5750         aPrms.clear();
5751         aItN = locMeshDS->GetNodes();
5752         while ( aItN->more() ) {
5753           const SMDS_MeshNode*     pNode = aItN->next();
5754           const SMDS_EdgePosition* pEPos =
5755             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5756           double aT = pEPos->GetUParameter();
5757           aPrms.push_back( aT );
5758         }
5759         list<SMESH_MeshEditor_PathPoint> LPP;
5760         //Extrusion_Error err =
5761         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5762         LLPPs.push_back(LPP);
5763         UsedNums.Add(k);
5764         // update startN for search following egde
5765         if ( aN1isOK ) aVprev = aV2;
5766         else           aVprev = aV1;
5767         break;
5768       }
5769     }
5770     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5771     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5772     fullList.splice( fullList.end(), firstList );
5773
5774     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5775     fullList.pop_back();
5776     itLLPP++;
5777     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5778       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5779       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5780       gp_Dir D1 = PP1.Tangent();
5781       gp_Dir D2 = PP2.Tangent();
5782       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5783       PP1.SetTangent(Dnew);
5784       fullList.push_back(PP1);
5785       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5786       PP1 = fullList.back();
5787       fullList.pop_back();
5788     }
5789     // if wire not closed
5790     fullList.push_back(PP1);
5791     // else ???
5792   }
5793   else {
5794     return EXTR_BAD_PATH_SHAPE;
5795   }
5796
5797   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5798                           theHasRefPoint, theRefPoint, theMakeGroups);
5799 }
5800
5801
5802 //=======================================================================
5803 //function : MakeEdgePathPoints
5804 //purpose  : auxilary for ExtrusionAlongTrack
5805 //=======================================================================
5806 SMESH_MeshEditor::Extrusion_Error
5807 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
5808                                      const TopoDS_Edge&                aTrackEdge,
5809                                      bool                              FirstIsStart,
5810                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5811 {
5812   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5813   aTolVec=1.e-7;
5814   aTolVec2=aTolVec*aTolVec;
5815   double aT1, aT2;
5816   TopoDS_Vertex aV1, aV2;
5817   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5818   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5819   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5820   // 2. Collect parameters on the track edge
5821   aPrms.push_front( aT1 );
5822   aPrms.push_back( aT2 );
5823   // sort parameters
5824   aPrms.sort();
5825   if( FirstIsStart ) {
5826     if ( aT1 > aT2 ) {
5827       aPrms.reverse();
5828     }
5829   }
5830   else {
5831     if ( aT2 > aT1 ) {
5832       aPrms.reverse();
5833     }
5834   }
5835   // 3. Path Points
5836   SMESH_MeshEditor_PathPoint aPP;
5837   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5838   std::list<double>::iterator aItD = aPrms.begin();
5839   for(; aItD != aPrms.end(); ++aItD) {
5840     double aT = *aItD;
5841     gp_Pnt aP3D;
5842     gp_Vec aVec;
5843     aC3D->D1( aT, aP3D, aVec );
5844     aL2 = aVec.SquareMagnitude();
5845     if ( aL2 < aTolVec2 )
5846       return EXTR_CANT_GET_TANGENT;
5847     gp_Dir aTgt( aVec );
5848     aPP.SetPnt( aP3D );
5849     aPP.SetTangent( aTgt );
5850     aPP.SetParameter( aT );
5851     LPP.push_back(aPP);
5852   }
5853   return EXTR_OK;
5854 }
5855
5856
5857 //=======================================================================
5858 //function : MakeExtrElements
5859 //purpose  : auxilary for ExtrusionAlongTrack
5860 //=======================================================================
5861 SMESH_MeshEditor::Extrusion_Error
5862 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&                 theElements,
5863                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5864                                    const bool                        theHasAngles,
5865                                    list<double>&                     theAngles,
5866                                    const bool                        theLinearVariation,
5867                                    const bool                        theHasRefPoint,
5868                                    const gp_Pnt&                     theRefPoint,
5869                                    const bool                        theMakeGroups)
5870 {
5871   const int aNbTP = fullList.size();
5872   // Angles
5873   if( theHasAngles && !theAngles.empty() && theLinearVariation )
5874     LinearAngleVariation(aNbTP-1, theAngles);
5875   // fill vector of path points with angles
5876   vector<SMESH_MeshEditor_PathPoint> aPPs;
5877   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5878   list<double>::iterator                 itAngles = theAngles.begin();
5879   aPPs.push_back( *itPP++ );
5880   for( ; itPP != fullList.end(); itPP++) {
5881     aPPs.push_back( *itPP );
5882     if ( theHasAngles && itAngles != theAngles.end() )
5883       aPPs.back().SetAngle( *itAngles++ );
5884   }
5885
5886   TNodeOfNodeListMap   mapNewNodes;
5887   TElemOfVecOfNnlmiMap mapElemNewNodes;
5888   TTElemOfElemListMap  newElemsMap;
5889   TIDSortedElemSet::iterator itElem;
5890   // source elements for each generated one
5891   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5892
5893   // 3. Center of rotation aV0
5894   gp_Pnt aV0 = theRefPoint;
5895   if ( !theHasRefPoint )
5896   {
5897     gp_XYZ aGC( 0.,0.,0. );
5898     TIDSortedElemSet newNodes;
5899
5900     itElem = theElements.begin();
5901     for ( ; itElem != theElements.end(); itElem++ ) {
5902       const SMDS_MeshElement* elem = *itElem;
5903
5904       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5905       while ( itN->more() ) {
5906         const SMDS_MeshElement* node = itN->next();
5907         if ( newNodes.insert( node ).second )
5908           aGC += SMESH_TNodeXYZ( node );
5909       }
5910     }
5911     aGC /= newNodes.size();
5912     aV0.SetXYZ( aGC );
5913   } // if (!theHasRefPoint) {
5914
5915   // 4. Processing the elements
5916   SMESHDS_Mesh* aMesh = GetMeshDS();
5917
5918   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5919     // check element type
5920     const SMDS_MeshElement* elem = *itElem;
5921     SMDSAbs_ElementType   aTypeE = elem->GetType();
5922     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5923       continue;
5924
5925     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5926     newNodesItVec.reserve( elem->NbNodes() );
5927
5928     // loop on elem nodes
5929     int nodeIndex = -1;
5930     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5931     while ( itN->more() )
5932     {
5933       ++nodeIndex;
5934       // check if a node has been already processed
5935       const SMDS_MeshNode* node =
5936         static_cast<const SMDS_MeshNode*>( itN->next() );
5937       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5938       if ( nIt == mapNewNodes.end() ) {
5939         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5940         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5941
5942         // make new nodes
5943         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5944         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5945         gp_Ax1 anAx1, anAxT1T0;
5946         gp_Dir aDT1x, aDT0x, aDT1T0;
5947
5948         aTolAng=1.e-4;
5949
5950         aV0x = aV0;
5951         aPN0 = SMESH_TNodeXYZ( node );
5952
5953         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5954         aP0x = aPP0.Pnt();
5955         aDT0x= aPP0.Tangent();
5956         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5957
5958         for ( int j = 1; j < aNbTP; ++j ) {
5959           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5960           aP1x     = aPP1.Pnt();
5961           aDT1x    = aPP1.Tangent();
5962           aAngle1x = aPP1.Angle();
5963
5964           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5965           // Translation
5966           gp_Vec aV01x( aP0x, aP1x );
5967           aTrsf.SetTranslation( aV01x );
5968
5969           // traslated point
5970           aV1x = aV0x.Transformed( aTrsf );
5971           aPN1 = aPN0.Transformed( aTrsf );
5972
5973           // rotation 1 [ T1,T0 ]
5974           aAngleT1T0=-aDT1x.Angle( aDT0x );
5975           if (fabs(aAngleT1T0) > aTolAng) {
5976             aDT1T0=aDT1x^aDT0x;
5977             anAxT1T0.SetLocation( aV1x );
5978             anAxT1T0.SetDirection( aDT1T0 );
5979             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5980
5981             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5982           }
5983
5984           // rotation 2
5985           if ( theHasAngles ) {
5986             anAx1.SetLocation( aV1x );
5987             anAx1.SetDirection( aDT1x );
5988             aTrsfRot.SetRotation( anAx1, aAngle1x );
5989
5990             aPN1 = aPN1.Transformed( aTrsfRot );
5991           }
5992
5993           // make new node
5994           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5995           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5996             // create additional node
5997             double x = ( aPN1.X() + aPN0.X() )/2.;
5998             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5999             double z = ( aPN1.Z() + aPN0.Z() )/2.;
6000             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6001             myLastCreatedNodes.Append(newNode);
6002             srcNodes.Append( node );
6003             listNewNodes.push_back( newNode );
6004           }
6005           const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6006           myLastCreatedNodes.Append(newNode);
6007           srcNodes.Append( node );
6008           listNewNodes.push_back( newNode );
6009
6010           aPN0 = aPN1;
6011           aP0x = aP1x;
6012           aV0x = aV1x;
6013           aDT0x = aDT1x;
6014         }
6015       }
6016
6017       else {
6018         // if current elem is quadratic and current node is not medium
6019         // we have to check - may be it is needed to insert additional nodes
6020         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6021           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6022           if(listNewNodes.size()==aNbTP-1) {
6023             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6024             gp_XYZ P(node->X(), node->Y(), node->Z());
6025             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6026             int i;
6027             for(i=0; i<aNbTP-1; i++) {
6028               const SMDS_MeshNode* N = *it;
6029               double x = ( N->X() + P.X() )/2.;
6030               double y = ( N->Y() + P.Y() )/2.;
6031               double z = ( N->Z() + P.Z() )/2.;
6032               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6033               srcNodes.Append( node );
6034               myLastCreatedNodes.Append(newN);
6035               aNodes[2*i] = newN;
6036               aNodes[2*i+1] = N;
6037               P = gp_XYZ(N->X(),N->Y(),N->Z());
6038             }
6039             listNewNodes.clear();
6040             for(i=0; i<2*(aNbTP-1); i++) {
6041               listNewNodes.push_back(aNodes[i]);
6042             }
6043           }
6044         }
6045       }
6046
6047       newNodesItVec.push_back( nIt );
6048     }
6049     // make new elements
6050     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6051     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6052     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6053   }
6054
6055   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6056
6057   if ( theMakeGroups )
6058     generateGroups( srcNodes, srcElems, "extruded");
6059
6060   return EXTR_OK;
6061 }
6062
6063
6064 //=======================================================================
6065 //function : LinearAngleVariation
6066 //purpose  : auxilary for ExtrusionAlongTrack
6067 //=======================================================================
6068 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6069                                             list<double>& Angles)
6070 {
6071   int nbAngles = Angles.size();
6072   if( nbSteps > nbAngles ) {
6073     vector<double> theAngles(nbAngles);
6074     list<double>::iterator it = Angles.begin();
6075     int i = -1;
6076     for(; it!=Angles.end(); it++) {
6077       i++;
6078       theAngles[i] = (*it);
6079     }
6080     list<double> res;
6081     double rAn2St = double( nbAngles ) / double( nbSteps );
6082     double angPrev = 0, angle;
6083     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6084       double angCur = rAn2St * ( iSt+1 );
6085       double angCurFloor  = floor( angCur );
6086       double angPrevFloor = floor( angPrev );
6087       if ( angPrevFloor == angCurFloor )
6088         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6089       else {
6090         int iP = int( angPrevFloor );
6091         double angPrevCeil = ceil(angPrev);
6092         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6093
6094         int iC = int( angCurFloor );
6095         if ( iC < nbAngles )
6096           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6097
6098         iP = int( angPrevCeil );
6099         while ( iC-- > iP )
6100           angle += theAngles[ iC ];
6101       }
6102       res.push_back(angle);
6103       angPrev = angCur;
6104     }
6105     Angles.clear();
6106     it = res.begin();
6107     for(; it!=res.end(); it++)
6108       Angles.push_back( *it );
6109   }
6110 }
6111
6112
6113 //================================================================================
6114 /*!
6115  * \brief Move or copy theElements applying theTrsf to their nodes
6116  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6117  *  \param theTrsf - transformation to apply
6118  *  \param theCopy - if true, create translated copies of theElems
6119  *  \param theMakeGroups - if true and theCopy, create translated groups
6120  *  \param theTargetMesh - mesh to copy translated elements into
6121  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6122  */
6123 //================================================================================
6124
6125 SMESH_MeshEditor::PGroupIDs
6126 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6127                              const gp_Trsf&     theTrsf,
6128                              const bool         theCopy,
6129                              const bool         theMakeGroups,
6130                              SMESH_Mesh*        theTargetMesh)
6131 {
6132   myLastCreatedElems.Clear();
6133   myLastCreatedNodes.Clear();
6134
6135   bool needReverse = false;
6136   string groupPostfix;
6137   switch ( theTrsf.Form() ) {
6138   case gp_PntMirror:
6139     MESSAGE("gp_PntMirror");
6140     needReverse = true;
6141     groupPostfix = "mirrored";
6142     break;
6143   case gp_Ax1Mirror:
6144     MESSAGE("gp_Ax1Mirror");
6145     groupPostfix = "mirrored";
6146     break;
6147   case gp_Ax2Mirror:
6148     MESSAGE("gp_Ax2Mirror");
6149     needReverse = true;
6150     groupPostfix = "mirrored";
6151     break;
6152   case gp_Rotation:
6153     MESSAGE("gp_Rotation");
6154     groupPostfix = "rotated";
6155     break;
6156   case gp_Translation:
6157     MESSAGE("gp_Translation");
6158     groupPostfix = "translated";
6159     break;
6160   case gp_Scale:
6161     MESSAGE("gp_Scale");
6162     groupPostfix = "scaled";
6163     break;
6164   case gp_CompoundTrsf: // different scale by axis
6165     MESSAGE("gp_CompoundTrsf");
6166     groupPostfix = "scaled";
6167     break;
6168   default:
6169     MESSAGE("default");
6170     needReverse = false;
6171     groupPostfix = "transformed";
6172   }
6173
6174   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6175   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6176   SMESHDS_Mesh* aMesh    = GetMeshDS();
6177
6178
6179   // map old node to new one
6180   TNodeNodeMap nodeMap;
6181
6182   // elements sharing moved nodes; those of them which have all
6183   // nodes mirrored but are not in theElems are to be reversed
6184   TIDSortedElemSet inverseElemSet;
6185
6186   // source elements for each generated one
6187   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6188
6189   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6190   TIDSortedElemSet orphanNode;
6191
6192   if ( theElems.empty() ) // transform the whole mesh
6193   {
6194     // add all elements
6195     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6196     while ( eIt->more() ) theElems.insert( eIt->next() );
6197     // add orphan nodes
6198     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6199     while ( nIt->more() )
6200     {
6201       const SMDS_MeshNode* node = nIt->next();
6202       if ( node->NbInverseElements() == 0)
6203         orphanNode.insert( node );
6204     }
6205   }
6206
6207   // loop on elements to transform nodes : first orphan nodes then elems
6208   TIDSortedElemSet::iterator itElem;
6209   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6210   for (int i=0; i<2; i++)
6211   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6212     const SMDS_MeshElement* elem = *itElem;
6213     if ( !elem )
6214       continue;
6215
6216     // loop on elem nodes
6217     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6218     while ( itN->more() ) {
6219
6220       const SMDS_MeshNode* node = cast2Node( itN->next() );
6221       // check if a node has been already transformed
6222       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6223         nodeMap.insert( make_pair ( node, node ));
6224       if ( !n2n_isnew.second )
6225         continue;
6226
6227       double coord[3];
6228       coord[0] = node->X();
6229       coord[1] = node->Y();
6230       coord[2] = node->Z();
6231       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6232       if ( theTargetMesh ) {
6233         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6234         n2n_isnew.first->second = newNode;
6235         myLastCreatedNodes.Append(newNode);
6236         srcNodes.Append( node );
6237       }
6238       else if ( theCopy ) {
6239         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6240         n2n_isnew.first->second = newNode;
6241         myLastCreatedNodes.Append(newNode);
6242         srcNodes.Append( node );
6243       }
6244       else {
6245         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6246         // node position on shape becomes invalid
6247         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6248           ( SMDS_SpacePosition::originSpacePosition() );
6249       }
6250
6251       // keep inverse elements
6252       if ( !theCopy && !theTargetMesh && needReverse ) {
6253         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6254         while ( invElemIt->more() ) {
6255           const SMDS_MeshElement* iel = invElemIt->next();
6256           inverseElemSet.insert( iel );
6257         }
6258       }
6259     }
6260   }
6261
6262   // either create new elements or reverse mirrored ones
6263   if ( !theCopy && !needReverse && !theTargetMesh )
6264     return PGroupIDs();
6265
6266   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6267   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6268     theElems.insert( *invElemIt );
6269
6270   // Replicate or reverse elements
6271
6272   std::vector<int> iForw;
6273   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6274   {
6275     const SMDS_MeshElement* elem = *itElem;
6276     if ( !elem ) continue;
6277
6278     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6279     int                  nbNodes  = elem->NbNodes();
6280     if ( geomType == SMDSGeom_NONE ) continue; // node
6281
6282     switch ( geomType ) {
6283
6284     case SMDSGeom_POLYGON:  // ---------------------- polygon
6285       {
6286         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6287         int iNode = 0;
6288         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6289         while (itN->more()) {
6290           const SMDS_MeshNode* node =
6291             static_cast<const SMDS_MeshNode*>(itN->next());
6292           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6293           if (nodeMapIt == nodeMap.end())
6294             break; // not all nodes transformed
6295           if (needReverse) {
6296             // reverse mirrored faces and volumes
6297             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6298           } else {
6299             poly_nodes[iNode] = (*nodeMapIt).second;
6300           }
6301           iNode++;
6302         }
6303         if ( iNode != nbNodes )
6304           continue; // not all nodes transformed
6305
6306         if ( theTargetMesh ) {
6307           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6308           srcElems.Append( elem );
6309         }
6310         else if ( theCopy ) {
6311           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6312           srcElems.Append( elem );
6313         }
6314         else {
6315           aMesh->ChangePolygonNodes(elem, poly_nodes);
6316         }
6317       }
6318       break;
6319
6320     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6321       {
6322         const SMDS_VtkVolume* aPolyedre =
6323           dynamic_cast<const SMDS_VtkVolume*>( elem );
6324         if (!aPolyedre) {
6325           MESSAGE("Warning: bad volumic element");
6326           continue;
6327         }
6328
6329         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6330         vector<int> quantities; quantities.reserve( nbNodes );
6331
6332         bool allTransformed = true;
6333         int nbFaces = aPolyedre->NbFaces();
6334         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6335           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6336           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6337             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6338             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6339             if (nodeMapIt == nodeMap.end()) {
6340               allTransformed = false; // not all nodes transformed
6341             } else {
6342               poly_nodes.push_back((*nodeMapIt).second);
6343             }
6344             if ( needReverse && allTransformed )
6345               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6346           }
6347           quantities.push_back(nbFaceNodes);
6348         }
6349         if ( !allTransformed )
6350           continue; // not all nodes transformed
6351
6352         if ( theTargetMesh ) {
6353           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6354           srcElems.Append( elem );
6355         }
6356         else if ( theCopy ) {
6357           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6358           srcElems.Append( elem );
6359         }
6360         else {
6361           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6362         }
6363       }
6364       break;
6365
6366     case SMDSGeom_BALL: // -------------------- Ball
6367       {
6368         if ( !theCopy && !theTargetMesh ) continue;
6369
6370         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6371         if (nodeMapIt == nodeMap.end())
6372           continue; // not all nodes transformed
6373
6374         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6375         if ( theTargetMesh ) {
6376           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6377           srcElems.Append( elem );
6378         }
6379         else {
6380           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6381           srcElems.Append( elem );
6382         }
6383       }
6384       break;
6385
6386     default: // ----------------------- Regular elements
6387
6388       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6389       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6390       const std::vector<int>& i = needReverse ? iRev : iForw;
6391
6392       // find transformed nodes
6393       vector<const SMDS_MeshNode*> nodes(nbNodes);
6394       int iNode = 0;
6395       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6396       while ( itN->more() ) {
6397         const SMDS_MeshNode* node =
6398           static_cast<const SMDS_MeshNode*>( itN->next() );
6399         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6400         if ( nodeMapIt == nodeMap.end() )
6401           break; // not all nodes transformed
6402         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6403       }
6404       if ( iNode != nbNodes )
6405         continue; // not all nodes transformed
6406
6407       if ( theTargetMesh ) {
6408         if ( SMDS_MeshElement* copy =
6409              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6410           myLastCreatedElems.Append( copy );
6411           srcElems.Append( elem );
6412         }
6413       }
6414       else if ( theCopy ) {
6415         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6416           srcElems.Append( elem );
6417       }
6418       else {
6419         // reverse element as it was reversed by transformation
6420         if ( nbNodes > 2 )
6421           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6422       }
6423     } // switch ( geomType )
6424
6425   } // loop on elements
6426
6427   PGroupIDs newGroupIDs;
6428
6429   if ( ( theMakeGroups && theCopy ) ||
6430        ( theMakeGroups && theTargetMesh ) )
6431     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
6432
6433   return newGroupIDs;
6434 }
6435
6436 //=======================================================================
6437 /*!
6438  * \brief Create groups of elements made during transformation
6439  * \param nodeGens - nodes making corresponding myLastCreatedNodes
6440  * \param elemGens - elements making corresponding myLastCreatedElems
6441  * \param postfix - to append to names of new groups
6442  */
6443 //=======================================================================
6444
6445 SMESH_MeshEditor::PGroupIDs
6446 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6447                                  const SMESH_SequenceOfElemPtr& elemGens,
6448                                  const std::string&             postfix,
6449                                  SMESH_Mesh*                    targetMesh)
6450 {
6451   PGroupIDs newGroupIDs( new list<int> );
6452   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6453
6454   // Sort existing groups by types and collect their names
6455
6456   // to store an old group and a generated new ones
6457   using boost::tuple;
6458   using boost::make_tuple;
6459   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6460   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6461   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6462   // group names
6463   set< string > groupNames;
6464
6465   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6466   if ( !groupIt->more() ) return newGroupIDs;
6467
6468   int newGroupID = mesh->GetGroupIds().back()+1;
6469   while ( groupIt->more() )
6470   {
6471     SMESH_Group * group = groupIt->next();
6472     if ( !group ) continue;
6473     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6474     if ( !groupDS || groupDS->IsEmpty() ) continue;
6475     groupNames.insert    ( group->GetName() );
6476     groupDS->SetStoreName( group->GetName() );
6477     const SMDSAbs_ElementType type = groupDS->GetType();
6478     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6479     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6480     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6481     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6482   }
6483
6484   // Loop on nodes and elements to add them in new groups
6485
6486   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6487   {
6488     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6489     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6490     if ( gens.Length() != elems.Length() )
6491       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6492
6493     // loop on created elements
6494     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6495     {
6496       const SMDS_MeshElement* sourceElem = gens( iElem );
6497       if ( !sourceElem ) {
6498         MESSAGE("generateGroups(): NULL source element");
6499         continue;
6500       }
6501       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6502       if ( groupsOldNew.empty() ) { // no groups of this type at all
6503         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6504           ++iElem; // skip all elements made by sourceElem
6505         continue;
6506       }
6507       // collect all elements made by the iElem-th sourceElem
6508       list< const SMDS_MeshElement* > resultElems;
6509       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6510         if ( resElem != sourceElem )
6511           resultElems.push_back( resElem );
6512       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6513         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6514           if ( resElem != sourceElem )
6515             resultElems.push_back( resElem );
6516
6517       // there must be a top element
6518       const SMDS_MeshElement* topElem = 0;
6519       if ( isNodes )
6520       {
6521         topElem = resultElems.back();
6522         resultElems.pop_back();
6523       }
6524       else
6525       {
6526         list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6527         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6528           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6529           {
6530             topElem = *resElemIt;
6531             resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6532             break;
6533           }
6534       }
6535
6536       // add resultElems to groups originted from ones the sourceElem belongs to
6537       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6538       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6539       {
6540         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6541         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6542         {
6543           // fill in a new group
6544           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6545           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6546           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6547             newGroup.Add( *resElemIt );
6548
6549           // fill a "top" group
6550           if ( topElem )
6551           {
6552             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6553             newTopGroup.Add( topElem );
6554           }
6555         }
6556       }
6557     } // loop on created elements
6558   }// loop on nodes and elements
6559
6560   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6561
6562   list<int> topGrouIds;
6563   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6564   {
6565     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6566     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6567                                       orderedOldNewGroups[i]->get<2>() };
6568     const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6569     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6570     {
6571       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6572       if ( newGroupDS->IsEmpty() )
6573       {
6574         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6575       }
6576       else
6577       {
6578         // set group type
6579         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6580
6581         // make a name
6582         const bool isTop = ( nbNewGroups == 2 &&
6583                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6584                              is2nd );
6585
6586         string name = oldGroupDS->GetStoreName();
6587         if ( !targetMesh ) {
6588           string suffix = ( isTop ? "top": postfix.c_str() );
6589           name += "_";
6590           name += suffix;
6591           int nb = 1;
6592           while ( !groupNames.insert( name ).second ) // name exists
6593             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6594         }
6595         else if ( isTop ) {
6596           name += "_top";
6597         }
6598         newGroupDS->SetStoreName( name.c_str() );
6599
6600         // make a SMESH_Groups
6601         mesh->AddGroup( newGroupDS );
6602         if ( isTop )
6603           topGrouIds.push_back( newGroupDS->GetID() );
6604         else
6605           newGroupIDs->push_back( newGroupDS->GetID() );
6606       }
6607     }
6608   }
6609   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6610
6611   return newGroupIDs;
6612 }
6613
6614 //================================================================================
6615 /*!
6616  * \brief Return list of group of nodes close to each other within theTolerance
6617  *        Search among theNodes or in the whole mesh if theNodes is empty using
6618  *        an Octree algorithm
6619  */
6620 //================================================================================
6621
6622 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6623                                             const double         theTolerance,
6624                                             TListOfListOfNodes & theGroupsOfNodes)
6625 {
6626   myLastCreatedElems.Clear();
6627   myLastCreatedNodes.Clear();
6628
6629   if ( theNodes.empty() )
6630   { // get all nodes in the mesh
6631     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6632     while ( nIt->more() )
6633       theNodes.insert( theNodes.end(),nIt->next());
6634   }
6635
6636   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6637 }
6638
6639 //=======================================================================
6640 //function : SimplifyFace
6641 //purpose  :
6642 //=======================================================================
6643
6644 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6645                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6646                                     vector<int>&                         quantities) const
6647 {
6648   int nbNodes = faceNodes.size();
6649
6650   if (nbNodes < 3)
6651     return 0;
6652
6653   set<const SMDS_MeshNode*> nodeSet;
6654
6655   // get simple seq of nodes
6656   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6657   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6658   int iSimple = 0, nbUnique = 0;
6659
6660   simpleNodes[iSimple++] = faceNodes[0];
6661   nbUnique++;
6662   for (int iCur = 1; iCur < nbNodes; iCur++) {
6663     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6664       simpleNodes[iSimple++] = faceNodes[iCur];
6665       if (nodeSet.insert( faceNodes[iCur] ).second)
6666         nbUnique++;
6667     }
6668   }
6669   int nbSimple = iSimple;
6670   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6671     nbSimple--;
6672     iSimple--;
6673   }
6674
6675   if (nbUnique < 3)
6676     return 0;
6677
6678   // separate loops
6679   int nbNew = 0;
6680   bool foundLoop = (nbSimple > nbUnique);
6681   while (foundLoop) {
6682     foundLoop = false;
6683     set<const SMDS_MeshNode*> loopSet;
6684     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6685       const SMDS_MeshNode* n = simpleNodes[iSimple];
6686       if (!loopSet.insert( n ).second) {
6687         foundLoop = true;
6688
6689         // separate loop
6690         int iC = 0, curLast = iSimple;
6691         for (; iC < curLast; iC++) {
6692           if (simpleNodes[iC] == n) break;
6693         }
6694         int loopLen = curLast - iC;
6695         if (loopLen > 2) {
6696           // create sub-element
6697           nbNew++;
6698           quantities.push_back(loopLen);
6699           for (; iC < curLast; iC++) {
6700             poly_nodes.push_back(simpleNodes[iC]);
6701           }
6702         }
6703         // shift the rest nodes (place from the first loop position)
6704         for (iC = curLast + 1; iC < nbSimple; iC++) {
6705           simpleNodes[iC - loopLen] = simpleNodes[iC];
6706         }
6707         nbSimple -= loopLen;
6708         iSimple -= loopLen;
6709       }
6710     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6711   } // while (foundLoop)
6712
6713   if (iSimple > 2) {
6714     nbNew++;
6715     quantities.push_back(iSimple);
6716     for (int i = 0; i < iSimple; i++)
6717       poly_nodes.push_back(simpleNodes[i]);
6718   }
6719
6720   return nbNew;
6721 }
6722
6723 //=======================================================================
6724 //function : MergeNodes
6725 //purpose  : In each group, the cdr of nodes are substituted by the first one
6726 //           in all elements.
6727 //=======================================================================
6728
6729 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6730 {
6731   MESSAGE("MergeNodes");
6732   myLastCreatedElems.Clear();
6733   myLastCreatedNodes.Clear();
6734
6735   SMESHDS_Mesh* aMesh = GetMeshDS();
6736
6737   TNodeNodeMap nodeNodeMap; // node to replace - new node
6738   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6739   list< int > rmElemIds, rmNodeIds;
6740
6741   // Fill nodeNodeMap and elems
6742
6743   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6744   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6745     list<const SMDS_MeshNode*>& nodes = *grIt;
6746     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6747     const SMDS_MeshNode* nToKeep = *nIt;
6748     //MESSAGE("node to keep " << nToKeep->GetID());
6749     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6750       const SMDS_MeshNode* nToRemove = *nIt;
6751       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6752       if ( nToRemove != nToKeep ) {
6753         //MESSAGE("  node to remove " << nToRemove->GetID());
6754         rmNodeIds.push_back( nToRemove->GetID() );
6755         AddToSameGroups( nToKeep, nToRemove, aMesh );
6756         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6757         // after MergeNodes() w/o creating node in place of merged ones.
6758         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6759         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6760           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6761             sm->SetIsAlwaysComputed( true );
6762       }
6763
6764       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6765       while ( invElemIt->more() ) {
6766         const SMDS_MeshElement* elem = invElemIt->next();
6767         elems.insert(elem);
6768       }
6769     }
6770   }
6771   // Change element nodes or remove an element
6772
6773   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6774   for ( ; eIt != elems.end(); eIt++ ) {
6775     const SMDS_MeshElement* elem = *eIt;
6776     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6777     int nbNodes = elem->NbNodes();
6778     int aShapeId = FindShape( elem );
6779
6780     set<const SMDS_MeshNode*> nodeSet;
6781     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6782     int iUnique = 0, iCur = 0, nbRepl = 0;
6783     vector<int> iRepl( nbNodes );
6784
6785     // get new seq of nodes
6786     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6787     while ( itN->more() ) {
6788       const SMDS_MeshNode* n =
6789         static_cast<const SMDS_MeshNode*>( itN->next() );
6790
6791       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6792       if ( nnIt != nodeNodeMap.end() ) { // n sticks
6793         n = (*nnIt).second;
6794         // BUG 0020185: begin
6795         {
6796           bool stopRecur = false;
6797           set<const SMDS_MeshNode*> nodesRecur;
6798           nodesRecur.insert(n);
6799           while (!stopRecur) {
6800             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6801             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6802               n = (*nnIt_i).second;
6803               if (!nodesRecur.insert(n).second) {
6804                 // error: recursive dependancy
6805                 stopRecur = true;
6806               }
6807             }
6808             else
6809               stopRecur = true;
6810           }
6811         }
6812         // BUG 0020185: end
6813       }
6814       curNodes[ iCur ] = n;
6815       bool isUnique = nodeSet.insert( n ).second;
6816       if ( isUnique )
6817         uniqueNodes[ iUnique++ ] = n;
6818       else
6819         iRepl[ nbRepl++ ] = iCur;
6820       iCur++;
6821     }
6822
6823     // Analyse element topology after replacement
6824
6825     bool isOk = true;
6826     int nbUniqueNodes = nodeSet.size();
6827     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6828     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6829       // Polygons and Polyhedral volumes
6830       if (elem->IsPoly()) {
6831
6832         if (elem->GetType() == SMDSAbs_Face) {
6833           // Polygon
6834           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6835           int inode = 0;
6836           for (; inode < nbNodes; inode++) {
6837             face_nodes[inode] = curNodes[inode];
6838           }
6839
6840           vector<const SMDS_MeshNode *> polygons_nodes;
6841           vector<int> quantities;
6842           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6843           if (nbNew > 0) {
6844             inode = 0;
6845             for (int iface = 0; iface < nbNew; iface++) {
6846               int nbNodes = quantities[iface];
6847               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6848               for (int ii = 0; ii < nbNodes; ii++, inode++) {
6849                 poly_nodes[ii] = polygons_nodes[inode];
6850               }
6851               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6852               myLastCreatedElems.Append(newElem);
6853               if (aShapeId)
6854                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6855             }
6856
6857             MESSAGE("ChangeElementNodes MergeNodes Polygon");
6858             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6859             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6860             int quid =0;
6861             if (nbNew > 0) quid = nbNew - 1;
6862             vector<int> newquant(quantities.begin()+quid, quantities.end());
6863             const SMDS_MeshElement* newElem = 0;
6864             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6865             myLastCreatedElems.Append(newElem);
6866             if ( aShapeId && newElem )
6867               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6868             rmElemIds.push_back(elem->GetID());
6869           }
6870           else {
6871             rmElemIds.push_back(elem->GetID());
6872           }
6873
6874         }
6875         else if (elem->GetType() == SMDSAbs_Volume) {
6876           // Polyhedral volume
6877           if (nbUniqueNodes < 4) {
6878             rmElemIds.push_back(elem->GetID());
6879           }
6880           else {
6881             // each face has to be analyzed in order to check volume validity
6882             const SMDS_VtkVolume* aPolyedre =
6883               dynamic_cast<const SMDS_VtkVolume*>( elem );
6884             if (aPolyedre) {
6885               int nbFaces = aPolyedre->NbFaces();
6886
6887               vector<const SMDS_MeshNode *> poly_nodes;
6888               vector<int> quantities;
6889
6890               for (int iface = 1; iface <= nbFaces; iface++) {
6891                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6892                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6893
6894                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6895                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6896                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6897                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6898                     faceNode = (*nnIt).second;
6899                   }
6900                   faceNodes[inode - 1] = faceNode;
6901                 }
6902
6903                 SimplifyFace(faceNodes, poly_nodes, quantities);
6904               }
6905
6906               if (quantities.size() > 3) {
6907                 // to be done: remove coincident faces
6908               }
6909
6910               if (quantities.size() > 3)
6911                 {
6912                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6913                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6914                   const SMDS_MeshElement* newElem = 0;
6915                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6916                   myLastCreatedElems.Append(newElem);
6917                   if ( aShapeId && newElem )
6918                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
6919                   rmElemIds.push_back(elem->GetID());
6920                 }
6921             }
6922             else {
6923               rmElemIds.push_back(elem->GetID());
6924             }
6925           }
6926         }
6927         else {
6928         }
6929
6930         continue;
6931       } // poly element
6932
6933       // Regular elements
6934       // TODO not all the possible cases are solved. Find something more generic?
6935       switch ( nbNodes ) {
6936       case 2: ///////////////////////////////////// EDGE
6937         isOk = false; break;
6938       case 3: ///////////////////////////////////// TRIANGLE
6939         isOk = false; break;
6940       case 4:
6941         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6942           isOk = false;
6943         else { //////////////////////////////////// QUADRANGLE
6944           if ( nbUniqueNodes < 3 )
6945             isOk = false;
6946           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6947             isOk = false; // opposite nodes stick
6948           //MESSAGE("isOk " << isOk);
6949         }
6950         break;
6951       case 6: ///////////////////////////////////// PENTAHEDRON
6952         if ( nbUniqueNodes == 4 ) {
6953           // ---------------------------------> tetrahedron
6954           if (nbRepl == 3 &&
6955               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6956             // all top nodes stick: reverse a bottom
6957             uniqueNodes[ 0 ] = curNodes [ 1 ];
6958             uniqueNodes[ 1 ] = curNodes [ 0 ];
6959           }
6960           else if (nbRepl == 3 &&
6961                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6962             // all bottom nodes stick: set a top before
6963             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6964             uniqueNodes[ 0 ] = curNodes [ 3 ];
6965             uniqueNodes[ 1 ] = curNodes [ 4 ];
6966             uniqueNodes[ 2 ] = curNodes [ 5 ];
6967           }
6968           else if (nbRepl == 4 &&
6969                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6970             // a lateral face turns into a line: reverse a bottom
6971             uniqueNodes[ 0 ] = curNodes [ 1 ];
6972             uniqueNodes[ 1 ] = curNodes [ 0 ];
6973           }
6974           else
6975             isOk = false;
6976         }
6977         else if ( nbUniqueNodes == 5 ) {
6978           // PENTAHEDRON --------------------> 2 tetrahedrons
6979           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6980             // a bottom node sticks with a linked top one
6981             // 1.
6982             SMDS_MeshElement* newElem =
6983               aMesh->AddVolume(curNodes[ 3 ],
6984                                curNodes[ 4 ],
6985                                curNodes[ 5 ],
6986                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6987             myLastCreatedElems.Append(newElem);
6988             if ( aShapeId )
6989               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6990             // 2. : reverse a bottom
6991             uniqueNodes[ 0 ] = curNodes [ 1 ];
6992             uniqueNodes[ 1 ] = curNodes [ 0 ];
6993             nbUniqueNodes = 4;
6994           }
6995           else
6996             isOk = false;
6997         }
6998         else
6999           isOk = false;
7000         break;
7001       case 8: {
7002         if(elem->IsQuadratic()) { // Quadratic quadrangle
7003           //   1    5    2
7004           //    +---+---+
7005           //    |       |
7006           //    |       |
7007           //   4+       +6
7008           //    |       |
7009           //    |       |
7010           //    +---+---+
7011           //   0    7    3
7012           isOk = false;
7013           if(nbRepl==2) {
7014             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7015           }
7016           if(nbRepl==3) {
7017             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7018             nbUniqueNodes = 6;
7019             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7020               uniqueNodes[0] = curNodes[0];
7021               uniqueNodes[1] = curNodes[2];
7022               uniqueNodes[2] = curNodes[3];
7023               uniqueNodes[3] = curNodes[5];
7024               uniqueNodes[4] = curNodes[6];
7025               uniqueNodes[5] = curNodes[7];
7026               isOk = true;
7027             }
7028             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7029               uniqueNodes[0] = curNodes[0];
7030               uniqueNodes[1] = curNodes[1];
7031               uniqueNodes[2] = curNodes[2];
7032               uniqueNodes[3] = curNodes[4];
7033               uniqueNodes[4] = curNodes[5];
7034               uniqueNodes[5] = curNodes[6];
7035               isOk = true;
7036             }
7037             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7038               uniqueNodes[0] = curNodes[1];
7039               uniqueNodes[1] = curNodes[2];
7040               uniqueNodes[2] = curNodes[3];
7041               uniqueNodes[3] = curNodes[5];
7042               uniqueNodes[4] = curNodes[6];
7043               uniqueNodes[5] = curNodes[0];
7044               isOk = true;
7045             }
7046             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7047               uniqueNodes[0] = curNodes[0];
7048               uniqueNodes[1] = curNodes[1];
7049               uniqueNodes[2] = curNodes[3];
7050               uniqueNodes[3] = curNodes[4];
7051               uniqueNodes[4] = curNodes[6];
7052               uniqueNodes[5] = curNodes[7];
7053               isOk = true;
7054             }
7055             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7056               uniqueNodes[0] = curNodes[0];
7057               uniqueNodes[1] = curNodes[2];
7058               uniqueNodes[2] = curNodes[3];
7059               uniqueNodes[3] = curNodes[1];
7060               uniqueNodes[4] = curNodes[6];
7061               uniqueNodes[5] = curNodes[7];
7062               isOk = true;
7063             }
7064             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7065               uniqueNodes[0] = curNodes[0];
7066               uniqueNodes[1] = curNodes[1];
7067               uniqueNodes[2] = curNodes[2];
7068               uniqueNodes[3] = curNodes[4];
7069               uniqueNodes[4] = curNodes[5];
7070               uniqueNodes[5] = curNodes[7];
7071               isOk = true;
7072             }
7073             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7074               uniqueNodes[0] = curNodes[0];
7075               uniqueNodes[1] = curNodes[1];
7076               uniqueNodes[2] = curNodes[3];
7077               uniqueNodes[3] = curNodes[4];
7078               uniqueNodes[4] = curNodes[2];
7079               uniqueNodes[5] = curNodes[7];
7080               isOk = true;
7081             }
7082             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7083               uniqueNodes[0] = curNodes[0];
7084               uniqueNodes[1] = curNodes[1];
7085               uniqueNodes[2] = curNodes[2];
7086               uniqueNodes[3] = curNodes[4];
7087               uniqueNodes[4] = curNodes[5];
7088               uniqueNodes[5] = curNodes[3];
7089               isOk = true;
7090             }
7091           }
7092           if(nbRepl==4) {
7093             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7094           }
7095           if(nbRepl==5) {
7096             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7097           }
7098           break;
7099         }
7100         //////////////////////////////////// HEXAHEDRON
7101         isOk = false;
7102         SMDS_VolumeTool hexa (elem);
7103         hexa.SetExternalNormal();
7104         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7105           //////////////////////// HEX ---> 1 tetrahedron
7106           for ( int iFace = 0; iFace < 6; iFace++ ) {
7107             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7108             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7109                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7110                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7111               // one face turns into a point ...
7112               int iOppFace = hexa.GetOppFaceIndex( iFace );
7113               ind = hexa.GetFaceNodesIndices( iOppFace );
7114               int nbStick = 0;
7115               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7116                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7117                   nbStick++;
7118               }
7119               if ( nbStick == 1 ) {
7120                 // ... and the opposite one - into a triangle.
7121                 // set a top node
7122                 ind = hexa.GetFaceNodesIndices( iFace );
7123                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7124                 isOk = true;
7125               }
7126               break;
7127             }
7128           }
7129         }
7130         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7131           //////////////////////// HEX ---> 1 prism
7132           int nbTria = 0, iTria[3];
7133           const int *ind; // indices of face nodes
7134           // look for triangular faces
7135           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7136             ind = hexa.GetFaceNodesIndices( iFace );
7137             TIDSortedNodeSet faceNodes;
7138             for ( iCur = 0; iCur < 4; iCur++ )
7139               faceNodes.insert( curNodes[ind[iCur]] );
7140             if ( faceNodes.size() == 3 )
7141               iTria[ nbTria++ ] = iFace;
7142           }
7143           // check if triangles are opposite
7144           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7145           {
7146             isOk = true;
7147             // set nodes of the bottom triangle
7148             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7149             vector<int> indB;
7150             for ( iCur = 0; iCur < 4; iCur++ )
7151               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7152                 indB.push_back( ind[iCur] );
7153             if ( !hexa.IsForward() )
7154               std::swap( indB[0], indB[2] );
7155             for ( iCur = 0; iCur < 3; iCur++ )
7156               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7157             // set nodes of the top triangle
7158             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7159             for ( iCur = 0; iCur < 3; ++iCur )
7160               for ( int j = 0; j < 4; ++j )
7161                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7162                 {
7163                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7164                   break;
7165                 }
7166           }
7167           break;
7168         }
7169         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7170           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7171           for ( int iFace = 0; iFace < 6; iFace++ ) {
7172             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7173             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7174                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7175                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7176               // one face turns into a point ...
7177               int iOppFace = hexa.GetOppFaceIndex( iFace );
7178               ind = hexa.GetFaceNodesIndices( iOppFace );
7179               int nbStick = 0;
7180               iUnique = 2;  // reverse a tetrahedron 1 bottom
7181               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7182                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7183                   nbStick++;
7184                 else if ( iUnique >= 0 )
7185                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7186               }
7187               if ( nbStick == 0 ) {
7188                 // ... and the opposite one is a quadrangle
7189                 // set a top node
7190                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7191                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7192                 nbUniqueNodes = 4;
7193                 // tetrahedron 2
7194                 SMDS_MeshElement* newElem =
7195                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7196                                    curNodes[ind[ 3 ]],
7197                                    curNodes[ind[ 2 ]],
7198                                    curNodes[indTop[ 0 ]]);
7199                 myLastCreatedElems.Append(newElem);
7200                 if ( aShapeId )
7201                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7202                 isOk = true;
7203               }
7204               break;
7205             }
7206           }
7207         }
7208         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7209           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7210           // find indices of quad and tri faces
7211           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7212           for ( iFace = 0; iFace < 6; iFace++ ) {
7213             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7214             nodeSet.clear();
7215             for ( iCur = 0; iCur < 4; iCur++ )
7216               nodeSet.insert( curNodes[ind[ iCur ]] );
7217             nbUniqueNodes = nodeSet.size();
7218             if ( nbUniqueNodes == 3 )
7219               iTriFace[ nbTri++ ] = iFace;
7220             else if ( nbUniqueNodes == 4 )
7221               iQuadFace[ nbQuad++ ] = iFace;
7222           }
7223           if (nbQuad == 2 && nbTri == 4 &&
7224               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7225             // 2 opposite quadrangles stuck with a diagonal;
7226             // sample groups of merged indices: (0-4)(2-6)
7227             // --------------------------------------------> 2 tetrahedrons
7228             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7229             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7230             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7231             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7232                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7233               // stuck with 0-2 diagonal
7234               i0  = ind1[ 3 ];
7235               i1d = ind1[ 0 ];
7236               i2  = ind1[ 1 ];
7237               i3d = ind1[ 2 ];
7238               i0t = ind2[ 1 ];
7239               i2t = ind2[ 3 ];
7240             }
7241             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7242                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7243               // stuck with 1-3 diagonal
7244               i0  = ind1[ 0 ];
7245               i1d = ind1[ 1 ];
7246               i2  = ind1[ 2 ];
7247               i3d = ind1[ 3 ];
7248               i0t = ind2[ 0 ];
7249               i2t = ind2[ 1 ];
7250             }
7251             else {
7252               ASSERT(0);
7253             }
7254             // tetrahedron 1
7255             uniqueNodes[ 0 ] = curNodes [ i0 ];
7256             uniqueNodes[ 1 ] = curNodes [ i1d ];
7257             uniqueNodes[ 2 ] = curNodes [ i3d ];
7258             uniqueNodes[ 3 ] = curNodes [ i0t ];
7259             nbUniqueNodes = 4;
7260             // tetrahedron 2
7261             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7262                                                          curNodes[ i2 ],
7263                                                          curNodes[ i3d ],
7264                                                          curNodes[ i2t ]);
7265             myLastCreatedElems.Append(newElem);
7266             if ( aShapeId )
7267               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7268             isOk = true;
7269           }
7270           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7271                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7272             // --------------------------------------------> prism
7273             // find 2 opposite triangles
7274             nbUniqueNodes = 6;
7275             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7276               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7277                 // find indices of kept and replaced nodes
7278                 // and fill unique nodes of 2 opposite triangles
7279                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7280                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7281                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7282                 // fill unique nodes
7283                 iUnique = 0;
7284                 isOk = true;
7285                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7286                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7287                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7288                   if ( n == nInit ) {
7289                     // iCur of a linked node of the opposite face (make normals co-directed):
7290                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7291                     // check that correspondent corners of triangles are linked
7292                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7293                       isOk = false;
7294                     else {
7295                       uniqueNodes[ iUnique ] = n;
7296                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7297                       iUnique++;
7298                     }
7299                   }
7300                 }
7301                 break;
7302               }
7303             }
7304           }
7305         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7306         else
7307         {
7308           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7309         }
7310         break;
7311       } // HEXAHEDRON
7312
7313       default:
7314         isOk = false;
7315       } // switch ( nbNodes )
7316
7317     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7318
7319     if ( isOk ) { // the elem remains valid after sticking nodes
7320       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7321       {
7322         // Change nodes of polyedre
7323         const SMDS_VtkVolume* aPolyedre =
7324           dynamic_cast<const SMDS_VtkVolume*>( elem );
7325         if (aPolyedre) {
7326           int nbFaces = aPolyedre->NbFaces();
7327
7328           vector<const SMDS_MeshNode *> poly_nodes;
7329           vector<int> quantities (nbFaces);
7330
7331           for (int iface = 1; iface <= nbFaces; iface++) {
7332             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7333             quantities[iface - 1] = nbFaceNodes;
7334
7335             for (inode = 1; inode <= nbFaceNodes; inode++) {
7336               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7337
7338               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7339               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7340                 curNode = (*nnIt).second;
7341               }
7342               poly_nodes.push_back(curNode);
7343             }
7344           }
7345           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7346         }
7347       }
7348       else // replace non-polyhedron elements
7349       {
7350         const SMDSAbs_ElementType etyp = elem->GetType();
7351         const int elemId               = elem->GetID();
7352         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7353         uniqueNodes.resize(nbUniqueNodes);
7354
7355         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7356
7357         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7358         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7359         if ( sm && newElem )
7360           sm->AddElement( newElem );
7361         if ( elem != newElem )
7362           ReplaceElemInGroups( elem, newElem, aMesh );
7363       }
7364     }
7365     else {
7366       // Remove invalid regular element or invalid polygon
7367       rmElemIds.push_back( elem->GetID() );
7368     }
7369
7370   } // loop on elements
7371
7372   // Remove bad elements, then equal nodes (order important)
7373
7374   Remove( rmElemIds, false );
7375   Remove( rmNodeIds, true );
7376
7377 }
7378
7379
7380 // ========================================================
7381 // class   : SortableElement
7382 // purpose : allow sorting elements basing on their nodes
7383 // ========================================================
7384 class SortableElement : public set <const SMDS_MeshElement*>
7385 {
7386 public:
7387
7388   SortableElement( const SMDS_MeshElement* theElem )
7389   {
7390     myElem = theElem;
7391     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7392     while ( nodeIt->more() )
7393       this->insert( nodeIt->next() );
7394   }
7395
7396   const SMDS_MeshElement* Get() const
7397   { return myElem; }
7398
7399   void Set(const SMDS_MeshElement* e) const
7400   { myElem = e; }
7401
7402
7403 private:
7404   mutable const SMDS_MeshElement* myElem;
7405 };
7406
7407 //=======================================================================
7408 //function : FindEqualElements
7409 //purpose  : Return list of group of elements built on the same nodes.
7410 //           Search among theElements or in the whole mesh if theElements is empty
7411 //=======================================================================
7412
7413 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7414                                          TListOfListOfElementsID & theGroupsOfElementsID)
7415 {
7416   myLastCreatedElems.Clear();
7417   myLastCreatedNodes.Clear();
7418
7419   typedef map< SortableElement, int > TMapOfNodeSet;
7420   typedef list<int> TGroupOfElems;
7421
7422   if ( theElements.empty() )
7423   { // get all elements in the mesh
7424     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7425     while ( eIt->more() )
7426       theElements.insert( theElements.end(), eIt->next());
7427   }
7428
7429   vector< TGroupOfElems > arrayOfGroups;
7430   TGroupOfElems groupOfElems;
7431   TMapOfNodeSet mapOfNodeSet;
7432
7433   TIDSortedElemSet::iterator elemIt = theElements.begin();
7434   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7435     const SMDS_MeshElement* curElem = *elemIt;
7436     SortableElement SE(curElem);
7437     int ind = -1;
7438     // check uniqueness
7439     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7440     if( !(pp.second) ) {
7441       TMapOfNodeSet::iterator& itSE = pp.first;
7442       ind = (*itSE).second;
7443       arrayOfGroups[ind].push_back(curElem->GetID());
7444     }
7445     else {
7446       groupOfElems.clear();
7447       groupOfElems.push_back(curElem->GetID());
7448       arrayOfGroups.push_back(groupOfElems);
7449       i++;
7450     }
7451   }
7452
7453   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7454   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7455     groupOfElems = *groupIt;
7456     if ( groupOfElems.size() > 1 ) {
7457       groupOfElems.sort();
7458       theGroupsOfElementsID.push_back(groupOfElems);
7459     }
7460   }
7461 }
7462
7463 //=======================================================================
7464 //function : MergeElements
7465 //purpose  : In each given group, substitute all elements by the first one.
7466 //=======================================================================
7467
7468 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7469 {
7470   myLastCreatedElems.Clear();
7471   myLastCreatedNodes.Clear();
7472
7473   typedef list<int> TListOfIDs;
7474   TListOfIDs rmElemIds; // IDs of elems to remove
7475
7476   SMESHDS_Mesh* aMesh = GetMeshDS();
7477
7478   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7479   while ( groupsIt != theGroupsOfElementsID.end() ) {
7480     TListOfIDs& aGroupOfElemID = *groupsIt;
7481     aGroupOfElemID.sort();
7482     int elemIDToKeep = aGroupOfElemID.front();
7483     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7484     aGroupOfElemID.pop_front();
7485     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7486     while ( idIt != aGroupOfElemID.end() ) {
7487       int elemIDToRemove = *idIt;
7488       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7489       // add the kept element in groups of removed one (PAL15188)
7490       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7491       rmElemIds.push_back( elemIDToRemove );
7492       ++idIt;
7493     }
7494     ++groupsIt;
7495   }
7496
7497   Remove( rmElemIds, false );
7498 }
7499
7500 //=======================================================================
7501 //function : MergeEqualElements
7502 //purpose  : Remove all but one of elements built on the same nodes.
7503 //=======================================================================
7504
7505 void SMESH_MeshEditor::MergeEqualElements()
7506 {
7507   TIDSortedElemSet aMeshElements; /* empty input ==
7508                                      to merge equal elements in the whole mesh */
7509   TListOfListOfElementsID aGroupsOfElementsID;
7510   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7511   MergeElements(aGroupsOfElementsID);
7512 }
7513
7514 //=======================================================================
7515 //function : findAdjacentFace
7516 //purpose  :
7517 //=======================================================================
7518
7519 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7520                                                 const SMDS_MeshNode* n2,
7521                                                 const SMDS_MeshElement* elem)
7522 {
7523   TIDSortedElemSet elemSet, avoidSet;
7524   if ( elem )
7525     avoidSet.insert ( elem );
7526   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7527 }
7528
7529 //=======================================================================
7530 //function : FindFreeBorder
7531 //purpose  :
7532 //=======================================================================
7533
7534 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7535
7536 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7537                                        const SMDS_MeshNode*             theSecondNode,
7538                                        const SMDS_MeshNode*             theLastNode,
7539                                        list< const SMDS_MeshNode* > &   theNodes,
7540                                        list< const SMDS_MeshElement* >& theFaces)
7541 {
7542   if ( !theFirstNode || !theSecondNode )
7543     return false;
7544   // find border face between theFirstNode and theSecondNode
7545   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7546   if ( !curElem )
7547     return false;
7548
7549   theFaces.push_back( curElem );
7550   theNodes.push_back( theFirstNode );
7551   theNodes.push_back( theSecondNode );
7552
7553   //vector<const SMDS_MeshNode*> nodes;
7554   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7555   TIDSortedElemSet foundElems;
7556   bool needTheLast = ( theLastNode != 0 );
7557
7558   while ( nStart != theLastNode ) {
7559     if ( nStart == theFirstNode )
7560       return !needTheLast;
7561
7562     // find all free border faces sharing form nStart
7563
7564     list< const SMDS_MeshElement* > curElemList;
7565     list< const SMDS_MeshNode* > nStartList;
7566     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7567     while ( invElemIt->more() ) {
7568       const SMDS_MeshElement* e = invElemIt->next();
7569       if ( e == curElem || foundElems.insert( e ).second ) {
7570         // get nodes
7571         int iNode = 0, nbNodes = e->NbNodes();
7572         //const SMDS_MeshNode* nodes[nbNodes+1];
7573         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7574
7575         if(e->IsQuadratic()) {
7576           const SMDS_VtkFace* F =
7577             dynamic_cast<const SMDS_VtkFace*>(e);
7578           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7579           // use special nodes iterator
7580           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7581           while( anIter->more() ) {
7582             nodes[ iNode++ ] = cast2Node(anIter->next());
7583           }
7584         }
7585         else {
7586           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7587           while ( nIt->more() )
7588             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7589         }
7590         nodes[ iNode ] = nodes[ 0 ];
7591         // check 2 links
7592         for ( iNode = 0; iNode < nbNodes; iNode++ )
7593           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7594                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7595               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7596           {
7597             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7598             curElemList.push_back( e );
7599           }
7600       }
7601     }
7602     // analyse the found
7603
7604     int nbNewBorders = curElemList.size();
7605     if ( nbNewBorders == 0 ) {
7606       // no free border furthermore
7607       return !needTheLast;
7608     }
7609     else if ( nbNewBorders == 1 ) {
7610       // one more element found
7611       nIgnore = nStart;
7612       nStart = nStartList.front();
7613       curElem = curElemList.front();
7614       theFaces.push_back( curElem );
7615       theNodes.push_back( nStart );
7616     }
7617     else {
7618       // several continuations found
7619       list< const SMDS_MeshElement* >::iterator curElemIt;
7620       list< const SMDS_MeshNode* >::iterator nStartIt;
7621       // check if one of them reached the last node
7622       if ( needTheLast ) {
7623         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7624              curElemIt!= curElemList.end();
7625              curElemIt++, nStartIt++ )
7626           if ( *nStartIt == theLastNode ) {
7627             theFaces.push_back( *curElemIt );
7628             theNodes.push_back( *nStartIt );
7629             return true;
7630           }
7631       }
7632       // find the best free border by the continuations
7633       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7634       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7635       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7636            curElemIt!= curElemList.end();
7637            curElemIt++, nStartIt++ )
7638       {
7639         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7640         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7641         // find one more free border
7642         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7643           cNL->clear();
7644           cFL->clear();
7645         }
7646         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7647           // choice: clear a worse one
7648           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7649           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7650           contNodes[ iWorse ].clear();
7651           contFaces[ iWorse ].clear();
7652         }
7653       }
7654       if ( contNodes[0].empty() && contNodes[1].empty() )
7655         return false;
7656
7657       // append the best free border
7658       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7659       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7660       theNodes.pop_back(); // remove nIgnore
7661       theNodes.pop_back(); // remove nStart
7662       theFaces.pop_back(); // remove curElem
7663       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7664       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7665       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7666       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7667       return true;
7668
7669     } // several continuations found
7670   } // while ( nStart != theLastNode )
7671
7672   return true;
7673 }
7674
7675 //=======================================================================
7676 //function : CheckFreeBorderNodes
7677 //purpose  : Return true if the tree nodes are on a free border
7678 //=======================================================================
7679
7680 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7681                                             const SMDS_MeshNode* theNode2,
7682                                             const SMDS_MeshNode* theNode3)
7683 {
7684   list< const SMDS_MeshNode* > nodes;
7685   list< const SMDS_MeshElement* > faces;
7686   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7687 }
7688
7689 //=======================================================================
7690 //function : SewFreeBorder
7691 //purpose  :
7692 //=======================================================================
7693
7694 SMESH_MeshEditor::Sew_Error
7695 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7696                                  const SMDS_MeshNode* theBordSecondNode,
7697                                  const SMDS_MeshNode* theBordLastNode,
7698                                  const SMDS_MeshNode* theSideFirstNode,
7699                                  const SMDS_MeshNode* theSideSecondNode,
7700                                  const SMDS_MeshNode* theSideThirdNode,
7701                                  const bool           theSideIsFreeBorder,
7702                                  const bool           toCreatePolygons,
7703                                  const bool           toCreatePolyedrs)
7704 {
7705   myLastCreatedElems.Clear();
7706   myLastCreatedNodes.Clear();
7707
7708   MESSAGE("::SewFreeBorder()");
7709   Sew_Error aResult = SEW_OK;
7710
7711   // ====================================
7712   //    find side nodes and elements
7713   // ====================================
7714
7715   list< const SMDS_MeshNode* > nSide[ 2 ];
7716   list< const SMDS_MeshElement* > eSide[ 2 ];
7717   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7718   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7719
7720   // Free border 1
7721   // --------------
7722   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7723                       nSide[0], eSide[0])) {
7724     MESSAGE(" Free Border 1 not found " );
7725     aResult = SEW_BORDER1_NOT_FOUND;
7726   }
7727   if (theSideIsFreeBorder) {
7728     // Free border 2
7729     // --------------
7730     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7731                         nSide[1], eSide[1])) {
7732       MESSAGE(" Free Border 2 not found " );
7733       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7734     }
7735   }
7736   if ( aResult != SEW_OK )
7737     return aResult;
7738
7739   if (!theSideIsFreeBorder) {
7740     // Side 2
7741     // --------------
7742
7743     // -------------------------------------------------------------------------
7744     // Algo:
7745     // 1. If nodes to merge are not coincident, move nodes of the free border
7746     //    from the coord sys defined by the direction from the first to last
7747     //    nodes of the border to the correspondent sys of the side 2
7748     // 2. On the side 2, find the links most co-directed with the correspondent
7749     //    links of the free border
7750     // -------------------------------------------------------------------------
7751
7752     // 1. Since sewing may break if there are volumes to split on the side 2,
7753     //    we wont move nodes but just compute new coordinates for them
7754     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7755     TNodeXYZMap nBordXYZ;
7756     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7757     list< const SMDS_MeshNode* >::iterator nBordIt;
7758
7759     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7760     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7761     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7762     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7763     double tol2 = 1.e-8;
7764     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7765     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7766       // Need node movement.
7767
7768       // find X and Z axes to create trsf
7769       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7770       gp_Vec X = Zs ^ Zb;
7771       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7772         // Zb || Zs
7773         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7774
7775       // coord systems
7776       gp_Ax3 toBordAx( Pb1, Zb, X );
7777       gp_Ax3 fromSideAx( Ps1, Zs, X );
7778       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7779       // set trsf
7780       gp_Trsf toBordSys, fromSide2Sys;
7781       toBordSys.SetTransformation( toBordAx );
7782       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7783       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7784
7785       // move
7786       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7787         const SMDS_MeshNode* n = *nBordIt;
7788         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7789         toBordSys.Transforms( xyz );
7790         fromSide2Sys.Transforms( xyz );
7791         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7792       }
7793     }
7794     else {
7795       // just insert nodes XYZ in the nBordXYZ map
7796       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7797         const SMDS_MeshNode* n = *nBordIt;
7798         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7799       }
7800     }
7801
7802     // 2. On the side 2, find the links most co-directed with the correspondent
7803     //    links of the free border
7804
7805     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7806     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7807     sideNodes.push_back( theSideFirstNode );
7808
7809     bool hasVolumes = false;
7810     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7811     set<long> foundSideLinkIDs, checkedLinkIDs;
7812     SMDS_VolumeTool volume;
7813     //const SMDS_MeshNode* faceNodes[ 4 ];
7814
7815     const SMDS_MeshNode*    sideNode;
7816     const SMDS_MeshElement* sideElem;
7817     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7818     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7819     nBordIt = bordNodes.begin();
7820     nBordIt++;
7821     // border node position and border link direction to compare with
7822     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7823     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7824     // choose next side node by link direction or by closeness to
7825     // the current border node:
7826     bool searchByDir = ( *nBordIt != theBordLastNode );
7827     do {
7828       // find the next node on the Side 2
7829       sideNode = 0;
7830       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7831       long linkID;
7832       checkedLinkIDs.clear();
7833       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7834
7835       // loop on inverse elements of current node (prevSideNode) on the Side 2
7836       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7837       while ( invElemIt->more() )
7838       {
7839         const SMDS_MeshElement* elem = invElemIt->next();
7840         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7841         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7842         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7843         bool isVolume = volume.Set( elem );
7844         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7845         if ( isVolume ) // --volume
7846           hasVolumes = true;
7847         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7848           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7849           if(elem->IsQuadratic()) {
7850             const SMDS_VtkFace* F =
7851               dynamic_cast<const SMDS_VtkFace*>(elem);
7852             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7853             // use special nodes iterator
7854             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7855             while( anIter->more() ) {
7856               nodes[ iNode ] = cast2Node(anIter->next());
7857               if ( nodes[ iNode++ ] == prevSideNode )
7858                 iPrevNode = iNode - 1;
7859             }
7860           }
7861           else {
7862             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7863             while ( nIt->more() ) {
7864               nodes[ iNode ] = cast2Node( nIt->next() );
7865               if ( nodes[ iNode++ ] == prevSideNode )
7866                 iPrevNode = iNode - 1;
7867             }
7868           }
7869           // there are 2 links to check
7870           nbNodes = 2;
7871         }
7872         else // --edge
7873           continue;
7874         // loop on links, to be precise, on the second node of links
7875         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7876           const SMDS_MeshNode* n = nodes[ iNode ];
7877           if ( isVolume ) {
7878             if ( !volume.IsLinked( n, prevSideNode ))
7879               continue;
7880           }
7881           else {
7882             if ( iNode ) // a node before prevSideNode
7883               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7884             else         // a node after prevSideNode
7885               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7886           }
7887           // check if this link was already used
7888           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7889           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7890           if (!isJustChecked &&
7891               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7892           {
7893             // test a link geometrically
7894             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7895             bool linkIsBetter = false;
7896             double dot = 0.0, dist = 0.0;
7897             if ( searchByDir ) { // choose most co-directed link
7898               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7899               linkIsBetter = ( dot > maxDot );
7900             }
7901             else { // choose link with the node closest to bordPos
7902               dist = ( nextXYZ - bordPos ).SquareModulus();
7903               linkIsBetter = ( dist < minDist );
7904             }
7905             if ( linkIsBetter ) {
7906               maxDot = dot;
7907               minDist = dist;
7908               linkID = iLink;
7909               sideNode = n;
7910               sideElem = elem;
7911             }
7912           }
7913         }
7914       } // loop on inverse elements of prevSideNode
7915
7916       if ( !sideNode ) {
7917         MESSAGE(" Cant find path by links of the Side 2 ");
7918         return SEW_BAD_SIDE_NODES;
7919       }
7920       sideNodes.push_back( sideNode );
7921       sideElems.push_back( sideElem );
7922       foundSideLinkIDs.insert ( linkID );
7923       prevSideNode = sideNode;
7924
7925       if ( *nBordIt == theBordLastNode )
7926         searchByDir = false;
7927       else {
7928         // find the next border link to compare with
7929         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7930         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7931         // move to next border node if sideNode is before forward border node (bordPos)
7932         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7933           prevBordNode = *nBordIt;
7934           nBordIt++;
7935           bordPos = nBordXYZ[ *nBordIt ];
7936           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7937           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7938         }
7939       }
7940     }
7941     while ( sideNode != theSideSecondNode );
7942
7943     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7944       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7945       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7946     }
7947   } // end nodes search on the side 2
7948
7949   // ============================
7950   // sew the border to the side 2
7951   // ============================
7952
7953   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
7954   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7955
7956   TListOfListOfNodes nodeGroupsToMerge;
7957   if ( nbNodes[0] == nbNodes[1] ||
7958        ( theSideIsFreeBorder && !theSideThirdNode)) {
7959
7960     // all nodes are to be merged
7961
7962     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7963          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7964          nIt[0]++, nIt[1]++ )
7965     {
7966       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7967       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7968       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7969     }
7970   }
7971   else {
7972
7973     // insert new nodes into the border and the side to get equal nb of segments
7974
7975     // get normalized parameters of nodes on the borders
7976     //double param[ 2 ][ maxNbNodes ];
7977     double* param[ 2 ];
7978     param[0] = new double [ maxNbNodes ];
7979     param[1] = new double [ maxNbNodes ];
7980     int iNode, iBord;
7981     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7982       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7983       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7984       const SMDS_MeshNode* nPrev = *nIt;
7985       double bordLength = 0;
7986       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7987         const SMDS_MeshNode* nCur = *nIt;
7988         gp_XYZ segment (nCur->X() - nPrev->X(),
7989                         nCur->Y() - nPrev->Y(),
7990                         nCur->Z() - nPrev->Z());
7991         double segmentLen = segment.Modulus();
7992         bordLength += segmentLen;
7993         param[ iBord ][ iNode ] = bordLength;
7994         nPrev = nCur;
7995       }
7996       // normalize within [0,1]
7997       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
7998         param[ iBord ][ iNode ] /= bordLength;
7999       }
8000     }
8001
8002     // loop on border segments
8003     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8004     int i[ 2 ] = { 0, 0 };
8005     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8006     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8007
8008     TElemOfNodeListMap insertMap;
8009     TElemOfNodeListMap::iterator insertMapIt;
8010     // insertMap is
8011     // key:   elem to insert nodes into
8012     // value: 2 nodes to insert between + nodes to be inserted
8013     do {
8014       bool next[ 2 ] = { false, false };
8015
8016       // find min adjacent segment length after sewing
8017       double nextParam = 10., prevParam = 0;
8018       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8019         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8020           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8021         if ( i[ iBord ] > 0 )
8022           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8023       }
8024       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8025       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8026       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8027
8028       // choose to insert or to merge nodes
8029       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8030       if ( Abs( du ) <= minSegLen * 0.2 ) {
8031         // merge
8032         // ------
8033         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8034         const SMDS_MeshNode* n0 = *nIt[0];
8035         const SMDS_MeshNode* n1 = *nIt[1];
8036         nodeGroupsToMerge.back().push_back( n1 );
8037         nodeGroupsToMerge.back().push_back( n0 );
8038         // position of node of the border changes due to merge
8039         param[ 0 ][ i[0] ] += du;
8040         // move n1 for the sake of elem shape evaluation during insertion.
8041         // n1 will be removed by MergeNodes() anyway
8042         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8043         next[0] = next[1] = true;
8044       }
8045       else {
8046         // insert
8047         // ------
8048         int intoBord = ( du < 0 ) ? 0 : 1;
8049         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8050         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8051         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8052         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8053         if ( intoBord == 1 ) {
8054           // move node of the border to be on a link of elem of the side
8055           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8056           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8057           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8058           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8059           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8060         }
8061         insertMapIt = insertMap.find( elem );
8062         bool notFound = ( insertMapIt == insertMap.end() );
8063         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8064         if ( otherLink ) {
8065           // insert into another link of the same element:
8066           // 1. perform insertion into the other link of the elem
8067           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8068           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8069           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8070           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8071           // 2. perform insertion into the link of adjacent faces
8072           while (true) {
8073             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8074             if ( adjElem )
8075               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8076             else
8077               break;
8078           }
8079           if (toCreatePolyedrs) {
8080             // perform insertion into the links of adjacent volumes
8081             UpdateVolumes(n12, n22, nodeList);
8082           }
8083           // 3. find an element appeared on n1 and n2 after the insertion
8084           insertMap.erase( elem );
8085           elem = findAdjacentFace( n1, n2, 0 );
8086         }
8087         if ( notFound || otherLink ) {
8088           // add element and nodes of the side into the insertMap
8089           insertMapIt = insertMap.insert
8090             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8091           (*insertMapIt).second.push_back( n1 );
8092           (*insertMapIt).second.push_back( n2 );
8093         }
8094         // add node to be inserted into elem
8095         (*insertMapIt).second.push_back( nIns );
8096         next[ 1 - intoBord ] = true;
8097       }
8098
8099       // go to the next segment
8100       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8101         if ( next[ iBord ] ) {
8102           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8103             eIt[ iBord ]++;
8104           nPrev[ iBord ] = *nIt[ iBord ];
8105           nIt[ iBord ]++; i[ iBord ]++;
8106         }
8107       }
8108     }
8109     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8110
8111     // perform insertion of nodes into elements
8112
8113     for (insertMapIt = insertMap.begin();
8114          insertMapIt != insertMap.end();
8115          insertMapIt++ )
8116     {
8117       const SMDS_MeshElement* elem = (*insertMapIt).first;
8118       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8119       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8120       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8121
8122       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8123
8124       if ( !theSideIsFreeBorder ) {
8125         // look for and insert nodes into the faces adjacent to elem
8126         while (true) {
8127           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8128           if ( adjElem )
8129             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8130           else
8131             break;
8132         }
8133       }
8134       if (toCreatePolyedrs) {
8135         // perform insertion into the links of adjacent volumes
8136         UpdateVolumes(n1, n2, nodeList);
8137       }
8138     }
8139
8140     delete param[0];
8141     delete param[1];
8142   } // end: insert new nodes
8143
8144   MergeNodes ( nodeGroupsToMerge );
8145
8146   return aResult;
8147 }
8148
8149 //=======================================================================
8150 //function : InsertNodesIntoLink
8151 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8152 //           and theBetweenNode2 and split theElement
8153 //=======================================================================
8154
8155 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8156                                            const SMDS_MeshNode*        theBetweenNode1,
8157                                            const SMDS_MeshNode*        theBetweenNode2,
8158                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8159                                            const bool                  toCreatePoly)
8160 {
8161   if ( theFace->GetType() != SMDSAbs_Face ) return;
8162
8163   // find indices of 2 link nodes and of the rest nodes
8164   int iNode = 0, il1, il2, i3, i4;
8165   il1 = il2 = i3 = i4 = -1;
8166   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8167   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8168
8169   if(theFace->IsQuadratic()) {
8170     const SMDS_VtkFace* F =
8171       dynamic_cast<const SMDS_VtkFace*>(theFace);
8172     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8173     // use special nodes iterator
8174     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8175     while( anIter->more() ) {
8176       const SMDS_MeshNode* n = cast2Node(anIter->next());
8177       if ( n == theBetweenNode1 )
8178         il1 = iNode;
8179       else if ( n == theBetweenNode2 )
8180         il2 = iNode;
8181       else if ( i3 < 0 )
8182         i3 = iNode;
8183       else
8184         i4 = iNode;
8185       nodes[ iNode++ ] = n;
8186     }
8187   }
8188   else {
8189     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8190     while ( nodeIt->more() ) {
8191       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8192       if ( n == theBetweenNode1 )
8193         il1 = iNode;
8194       else if ( n == theBetweenNode2 )
8195         il2 = iNode;
8196       else if ( i3 < 0 )
8197         i3 = iNode;
8198       else
8199         i4 = iNode;
8200       nodes[ iNode++ ] = n;
8201     }
8202   }
8203   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8204     return ;
8205
8206   // arrange link nodes to go one after another regarding the face orientation
8207   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8208   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8209   if ( reverse ) {
8210     iNode = il1;
8211     il1 = il2;
8212     il2 = iNode;
8213     aNodesToInsert.reverse();
8214   }
8215   // check that not link nodes of a quadrangles are in good order
8216   int nbFaceNodes = theFace->NbNodes();
8217   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8218     iNode = i3;
8219     i3 = i4;
8220     i4 = iNode;
8221   }
8222
8223   if (toCreatePoly || theFace->IsPoly()) {
8224
8225     iNode = 0;
8226     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8227
8228     // add nodes of face up to first node of link
8229     bool isFLN = false;
8230
8231     if(theFace->IsQuadratic()) {
8232       const SMDS_VtkFace* F =
8233         dynamic_cast<const SMDS_VtkFace*>(theFace);
8234       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8235       // use special nodes iterator
8236       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8237       while( anIter->more()  && !isFLN ) {
8238         const SMDS_MeshNode* n = cast2Node(anIter->next());
8239         poly_nodes[iNode++] = n;
8240         if (n == nodes[il1]) {
8241           isFLN = true;
8242         }
8243       }
8244       // add nodes to insert
8245       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8246       for (; nIt != aNodesToInsert.end(); nIt++) {
8247         poly_nodes[iNode++] = *nIt;
8248       }
8249       // add nodes of face starting from last node of link
8250       while ( anIter->more() ) {
8251         poly_nodes[iNode++] = cast2Node(anIter->next());
8252       }
8253     }
8254     else {
8255       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8256       while ( nodeIt->more() && !isFLN ) {
8257         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8258         poly_nodes[iNode++] = n;
8259         if (n == nodes[il1]) {
8260           isFLN = true;
8261         }
8262       }
8263       // add nodes to insert
8264       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8265       for (; nIt != aNodesToInsert.end(); nIt++) {
8266         poly_nodes[iNode++] = *nIt;
8267       }
8268       // add nodes of face starting from last node of link
8269       while ( nodeIt->more() ) {
8270         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8271         poly_nodes[iNode++] = n;
8272       }
8273     }
8274
8275     // edit or replace the face
8276     SMESHDS_Mesh *aMesh = GetMeshDS();
8277
8278     if (theFace->IsPoly()) {
8279       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8280     }
8281     else {
8282       int aShapeId = FindShape( theFace );
8283
8284       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8285       myLastCreatedElems.Append(newElem);
8286       if ( aShapeId && newElem )
8287         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8288
8289       aMesh->RemoveElement(theFace);
8290     }
8291     return;
8292   }
8293
8294   SMESHDS_Mesh *aMesh = GetMeshDS();
8295   if( !theFace->IsQuadratic() ) {
8296
8297     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8298     int nbLinkNodes = 2 + aNodesToInsert.size();
8299     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8300     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8301     linkNodes[ 0 ] = nodes[ il1 ];
8302     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8303     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8304     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8305       linkNodes[ iNode++ ] = *nIt;
8306     }
8307     // decide how to split a quadrangle: compare possible variants
8308     // and choose which of splits to be a quadrangle
8309     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8310     if ( nbFaceNodes == 3 ) {
8311       iBestQuad = nbSplits;
8312       i4 = i3;
8313     }
8314     else if ( nbFaceNodes == 4 ) {
8315       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8316       double aBestRate = DBL_MAX;
8317       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8318         i1 = 0; i2 = 1;
8319         double aBadRate = 0;
8320         // evaluate elements quality
8321         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8322           if ( iSplit == iQuad ) {
8323             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8324                                    linkNodes[ i2++ ],
8325                                    nodes[ i3 ],
8326                                    nodes[ i4 ]);
8327             aBadRate += getBadRate( &quad, aCrit );
8328           }
8329           else {
8330             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8331                                    linkNodes[ i2++ ],
8332                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8333             aBadRate += getBadRate( &tria, aCrit );
8334           }
8335         }
8336         // choice
8337         if ( aBadRate < aBestRate ) {
8338           iBestQuad = iQuad;
8339           aBestRate = aBadRate;
8340         }
8341       }
8342     }
8343
8344     // create new elements
8345     int aShapeId = FindShape( theFace );
8346
8347     i1 = 0; i2 = 1;
8348     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8349       SMDS_MeshElement* newElem = 0;
8350       if ( iSplit == iBestQuad )
8351         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8352                                   linkNodes[ i2++ ],
8353                                   nodes[ i3 ],
8354                                   nodes[ i4 ]);
8355       else
8356         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8357                                   linkNodes[ i2++ ],
8358                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8359       myLastCreatedElems.Append(newElem);
8360       if ( aShapeId && newElem )
8361         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8362     }
8363
8364     // change nodes of theFace
8365     const SMDS_MeshNode* newNodes[ 4 ];
8366     newNodes[ 0 ] = linkNodes[ i1 ];
8367     newNodes[ 1 ] = linkNodes[ i2 ];
8368     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8369     newNodes[ 3 ] = nodes[ i4 ];
8370     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8371     const SMDS_MeshElement* newElem = 0;
8372     if (iSplit == iBestQuad)
8373       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8374     else
8375       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8376     myLastCreatedElems.Append(newElem);
8377     if ( aShapeId && newElem )
8378       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8379 } // end if(!theFace->IsQuadratic())
8380   else { // theFace is quadratic
8381     // we have to split theFace on simple triangles and one simple quadrangle
8382     int tmp = il1/2;
8383     int nbshift = tmp*2;
8384     // shift nodes in nodes[] by nbshift
8385     int i,j;
8386     for(i=0; i<nbshift; i++) {
8387       const SMDS_MeshNode* n = nodes[0];
8388       for(j=0; j<nbFaceNodes-1; j++) {
8389         nodes[j] = nodes[j+1];
8390       }
8391       nodes[nbFaceNodes-1] = n;
8392     }
8393     il1 = il1 - nbshift;
8394     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8395     //   n0      n1     n2    n0      n1     n2
8396     //     +-----+-----+        +-----+-----+
8397     //      \         /         |           |
8398     //       \       /          |           |
8399     //      n5+     +n3       n7+           +n3
8400     //         \   /            |           |
8401     //          \ /             |           |
8402     //           +              +-----+-----+
8403     //           n4           n6      n5     n4
8404
8405     // create new elements
8406     int aShapeId = FindShape( theFace );
8407
8408     int n1,n2,n3;
8409     if(nbFaceNodes==6) { // quadratic triangle
8410       SMDS_MeshElement* newElem =
8411         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8412       myLastCreatedElems.Append(newElem);
8413       if ( aShapeId && newElem )
8414         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8415       if(theFace->IsMediumNode(nodes[il1])) {
8416         // create quadrangle
8417         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8418         myLastCreatedElems.Append(newElem);
8419         if ( aShapeId && newElem )
8420           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8421         n1 = 1;
8422         n2 = 2;
8423         n3 = 3;
8424       }
8425       else {
8426         // create quadrangle
8427         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8428         myLastCreatedElems.Append(newElem);
8429         if ( aShapeId && newElem )
8430           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8431         n1 = 0;
8432         n2 = 1;
8433         n3 = 5;
8434       }
8435     }
8436     else { // nbFaceNodes==8 - quadratic quadrangle
8437       SMDS_MeshElement* newElem =
8438         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8439       myLastCreatedElems.Append(newElem);
8440       if ( aShapeId && newElem )
8441         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8442       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8443       myLastCreatedElems.Append(newElem);
8444       if ( aShapeId && newElem )
8445         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8446       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8447       myLastCreatedElems.Append(newElem);
8448       if ( aShapeId && newElem )
8449         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8450       if(theFace->IsMediumNode(nodes[il1])) {
8451         // create quadrangle
8452         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8453         myLastCreatedElems.Append(newElem);
8454         if ( aShapeId && newElem )
8455           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8456         n1 = 1;
8457         n2 = 2;
8458         n3 = 3;
8459       }
8460       else {
8461         // create quadrangle
8462         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8463         myLastCreatedElems.Append(newElem);
8464         if ( aShapeId && newElem )
8465           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8466         n1 = 0;
8467         n2 = 1;
8468         n3 = 7;
8469       }
8470     }
8471     // create needed triangles using n1,n2,n3 and inserted nodes
8472     int nbn = 2 + aNodesToInsert.size();
8473     //const SMDS_MeshNode* aNodes[nbn];
8474     vector<const SMDS_MeshNode*> aNodes(nbn);
8475     aNodes[0] = nodes[n1];
8476     aNodes[nbn-1] = nodes[n2];
8477     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8478     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8479       aNodes[iNode++] = *nIt;
8480     }
8481     for(i=1; i<nbn; i++) {
8482       SMDS_MeshElement* newElem =
8483         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8484       myLastCreatedElems.Append(newElem);
8485       if ( aShapeId && newElem )
8486         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8487     }
8488   }
8489   // remove old face
8490   aMesh->RemoveElement(theFace);
8491 }
8492
8493 //=======================================================================
8494 //function : UpdateVolumes
8495 //purpose  :
8496 //=======================================================================
8497 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8498                                       const SMDS_MeshNode*        theBetweenNode2,
8499                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8500 {
8501   myLastCreatedElems.Clear();
8502   myLastCreatedNodes.Clear();
8503
8504   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8505   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8506     const SMDS_MeshElement* elem = invElemIt->next();
8507
8508     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8509     SMDS_VolumeTool aVolume (elem);
8510     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8511       continue;
8512
8513     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8514     int iface, nbFaces = aVolume.NbFaces();
8515     vector<const SMDS_MeshNode *> poly_nodes;
8516     vector<int> quantities (nbFaces);
8517
8518     for (iface = 0; iface < nbFaces; iface++) {
8519       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8520       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8521       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8522
8523       for (int inode = 0; inode < nbFaceNodes; inode++) {
8524         poly_nodes.push_back(faceNodes[inode]);
8525
8526         if (nbInserted == 0) {
8527           if (faceNodes[inode] == theBetweenNode1) {
8528             if (faceNodes[inode + 1] == theBetweenNode2) {
8529               nbInserted = theNodesToInsert.size();
8530
8531               // add nodes to insert
8532               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8533               for (; nIt != theNodesToInsert.end(); nIt++) {
8534                 poly_nodes.push_back(*nIt);
8535               }
8536             }
8537           }
8538           else if (faceNodes[inode] == theBetweenNode2) {
8539             if (faceNodes[inode + 1] == theBetweenNode1) {
8540               nbInserted = theNodesToInsert.size();
8541
8542               // add nodes to insert in reversed order
8543               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8544               nIt--;
8545               for (; nIt != theNodesToInsert.begin(); nIt--) {
8546                 poly_nodes.push_back(*nIt);
8547               }
8548               poly_nodes.push_back(*nIt);
8549             }
8550           }
8551           else {
8552           }
8553         }
8554       }
8555       quantities[iface] = nbFaceNodes + nbInserted;
8556     }
8557
8558     // Replace or update the volume
8559     SMESHDS_Mesh *aMesh = GetMeshDS();
8560
8561     if (elem->IsPoly()) {
8562       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8563
8564     }
8565     else {
8566       int aShapeId = FindShape( elem );
8567
8568       SMDS_MeshElement* newElem =
8569         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8570       myLastCreatedElems.Append(newElem);
8571       if (aShapeId && newElem)
8572         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8573
8574       aMesh->RemoveElement(elem);
8575     }
8576   }
8577 }
8578
8579 namespace
8580 {
8581   //================================================================================
8582   /*!
8583    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8584    */
8585   //================================================================================
8586
8587   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8588                            vector<const SMDS_MeshNode *> & nodes,
8589                            vector<int> &                   nbNodeInFaces )
8590   {
8591     nodes.clear();
8592     nbNodeInFaces.clear();
8593     SMDS_VolumeTool vTool ( elem );
8594     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8595     {
8596       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8597       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8598       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8599     }
8600   }
8601 }
8602
8603 //=======================================================================
8604 /*!
8605  * \brief Convert elements contained in a submesh to quadratic
8606  * \return int - nb of checked elements
8607  */
8608 //=======================================================================
8609
8610 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8611                                              SMESH_MesherHelper& theHelper,
8612                                              const bool          theForce3d)
8613 {
8614   int nbElem = 0;
8615   if( !theSm ) return nbElem;
8616
8617   vector<int> nbNodeInFaces;
8618   vector<const SMDS_MeshNode *> nodes;
8619   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8620   while(ElemItr->more())
8621   {
8622     nbElem++;
8623     const SMDS_MeshElement* elem = ElemItr->next();
8624     if( !elem ) continue;
8625
8626     // analyse a necessity of conversion
8627     const SMDSAbs_ElementType aType = elem->GetType();
8628     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8629       continue;
8630     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8631     bool hasCentralNodes = false;
8632     if ( elem->IsQuadratic() )
8633     {
8634       bool alreadyOK;
8635       switch ( aGeomType ) {
8636       case SMDSEntity_Quad_Triangle:
8637       case SMDSEntity_Quad_Quadrangle:
8638       case SMDSEntity_Quad_Hexa:
8639         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8640
8641       case SMDSEntity_BiQuad_Triangle:
8642       case SMDSEntity_BiQuad_Quadrangle:
8643       case SMDSEntity_TriQuad_Hexa:
8644         alreadyOK = theHelper.GetIsBiQuadratic();
8645         hasCentralNodes = true;
8646         break;
8647       default:
8648         alreadyOK = true;
8649       }
8650       // take into account already present modium nodes
8651       switch ( aType ) {
8652       case SMDSAbs_Volume:
8653         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8654       case SMDSAbs_Face:
8655         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8656       case SMDSAbs_Edge:
8657         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8658       default:;
8659       }
8660       if ( alreadyOK )
8661         continue;
8662     }
8663     // get elem data needed to re-create it
8664     //
8665     const int id      = elem->GetID();
8666     const int nbNodes = elem->NbCornerNodes();
8667     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8668     if ( aGeomType == SMDSEntity_Polyhedra )
8669       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8670     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8671       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8672
8673     // remove a linear element
8674     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8675
8676     // remove central nodes of biquadratic elements (biquad->quad convertion)
8677     if ( hasCentralNodes )
8678       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8679         if ( nodes[i]->NbInverseElements() == 0 )
8680           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8681
8682     const SMDS_MeshElement* NewElem = 0;
8683
8684     switch( aType )
8685     {
8686     case SMDSAbs_Edge :
8687       {
8688         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8689         break;
8690       }
8691     case SMDSAbs_Face :
8692       {
8693         switch(nbNodes)
8694         {
8695         case 3:
8696           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8697           break;
8698         case 4:
8699           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8700           break;
8701         default:
8702           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8703         }
8704         break;
8705       }
8706     case SMDSAbs_Volume :
8707       {
8708         switch( aGeomType )
8709         {
8710         case SMDSEntity_Tetra:
8711           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8712           break;
8713         case SMDSEntity_Pyramid:
8714           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8715           break;
8716         case SMDSEntity_Penta:
8717           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8718           break;
8719         case SMDSEntity_Hexa:
8720         case SMDSEntity_Quad_Hexa:
8721         case SMDSEntity_TriQuad_Hexa:
8722           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8723                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8724           break;
8725         case SMDSEntity_Hexagonal_Prism:
8726         default:
8727           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8728         }
8729         break;
8730       }
8731     default :
8732       continue;
8733     }
8734     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8735     if( NewElem && NewElem->getshapeId() < 1 )
8736       theSm->AddElement( NewElem );
8737   }
8738   return nbElem;
8739 }
8740 //=======================================================================
8741 //function : ConvertToQuadratic
8742 //purpose  :
8743 //=======================================================================
8744
8745 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8746 {
8747   SMESHDS_Mesh* meshDS = GetMeshDS();
8748
8749   SMESH_MesherHelper aHelper(*myMesh);
8750
8751   aHelper.SetIsQuadratic( true );
8752   aHelper.SetIsBiQuadratic( theToBiQuad );
8753   aHelper.SetElementsOnShape(true);
8754   aHelper.ToFixNodeParameters( true );
8755
8756   // convert elements assigned to sub-meshes
8757   int nbCheckedElems = 0;
8758   if ( myMesh->HasShapeToMesh() )
8759   {
8760     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8761     {
8762       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8763       while ( smIt->more() ) {
8764         SMESH_subMesh* sm = smIt->next();
8765         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8766           aHelper.SetSubShape( sm->GetSubShape() );
8767           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8768         }
8769       }
8770     }
8771   }
8772
8773   // convert elements NOT assigned to sub-meshes
8774   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8775   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8776   {
8777     aHelper.SetElementsOnShape(false);
8778     SMESHDS_SubMesh *smDS = 0;
8779
8780     // convert edges
8781     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8782     while( aEdgeItr->more() )
8783     {
8784       const SMDS_MeshEdge* edge = aEdgeItr->next();
8785       if ( !edge->IsQuadratic() )
8786       {
8787         int                  id = edge->GetID();
8788         const SMDS_MeshNode* n1 = edge->GetNode(0);
8789         const SMDS_MeshNode* n2 = edge->GetNode(1);
8790
8791         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8792
8793         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8794         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8795       }
8796       else
8797       {
8798         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8799       }
8800     }
8801
8802     // convert faces
8803     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8804     while( aFaceItr->more() )
8805     {
8806       const SMDS_MeshFace* face = aFaceItr->next();
8807       if ( !face ) continue;
8808       
8809       const SMDSAbs_EntityType type = face->GetEntityType();
8810       bool alreadyOK;
8811       switch( type )
8812       {
8813       case SMDSEntity_Quad_Triangle:
8814       case SMDSEntity_Quad_Quadrangle:
8815         alreadyOK = !theToBiQuad;
8816         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8817         break;
8818       case SMDSEntity_BiQuad_Triangle:
8819       case SMDSEntity_BiQuad_Quadrangle:
8820         alreadyOK = theToBiQuad;
8821         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8822         break;
8823       default: alreadyOK = false;
8824       }
8825       if ( alreadyOK )
8826         continue;
8827
8828       const int id = face->GetID();
8829       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8830
8831       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8832
8833       SMDS_MeshFace * NewFace = 0;
8834       switch( type )
8835       {
8836       case SMDSEntity_Triangle:
8837       case SMDSEntity_Quad_Triangle:
8838       case SMDSEntity_BiQuad_Triangle:
8839         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8840         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8841           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8842         break;
8843
8844       case SMDSEntity_Quadrangle:
8845       case SMDSEntity_Quad_Quadrangle:
8846       case SMDSEntity_BiQuad_Quadrangle:
8847         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8848         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8849           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8850         break;
8851
8852       default:;
8853         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8854       }
8855       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8856     }
8857
8858     // convert volumes
8859     vector<int> nbNodeInFaces;
8860     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8861     while(aVolumeItr->more())
8862     {
8863       const SMDS_MeshVolume* volume = aVolumeItr->next();
8864       if ( !volume ) continue;
8865
8866       const SMDSAbs_EntityType type = volume->GetEntityType();
8867       if ( volume->IsQuadratic() )
8868       {
8869         bool alreadyOK;
8870         switch ( type )
8871         {
8872         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8873         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8874         default:                      alreadyOK = true;
8875         }
8876         if ( alreadyOK )
8877         {
8878           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8879           continue;
8880         }
8881       }
8882       const int id = volume->GetID();
8883       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8884       if ( type == SMDSEntity_Polyhedra )
8885         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8886       else if ( type == SMDSEntity_Hexagonal_Prism )
8887         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8888
8889       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8890
8891       SMDS_MeshVolume * NewVolume = 0;
8892       switch ( type )
8893       {
8894       case SMDSEntity_Tetra:
8895         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8896         break;
8897       case SMDSEntity_Hexa:
8898       case SMDSEntity_Quad_Hexa:
8899       case SMDSEntity_TriQuad_Hexa:
8900         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8901                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8902         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8903           if ( nodes[i]->NbInverseElements() == 0 )
8904             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8905         break;
8906       case SMDSEntity_Pyramid:
8907         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8908                                       nodes[3], nodes[4], id, theForce3d);
8909         break;
8910       case SMDSEntity_Penta:
8911         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8912                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8913         break;
8914       case SMDSEntity_Hexagonal_Prism:
8915       default:
8916         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8917       }
8918       ReplaceElemInGroups(volume, NewVolume, meshDS);
8919     }
8920   }
8921
8922   if ( !theForce3d )
8923   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8924     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8925     // aHelper.FixQuadraticElements(myError);
8926     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8927   }
8928 }
8929
8930 //================================================================================
8931 /*!
8932  * \brief Makes given elements quadratic
8933  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
8934  *  \param theElements - elements to make quadratic
8935  */
8936 //================================================================================
8937
8938 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
8939                                           TIDSortedElemSet& theElements,
8940                                           const bool        theToBiQuad)
8941 {
8942   if ( theElements.empty() ) return;
8943
8944   // we believe that all theElements are of the same type
8945   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8946
8947   // get all nodes shared by theElements
8948   TIDSortedNodeSet allNodes;
8949   TIDSortedElemSet::iterator eIt = theElements.begin();
8950   for ( ; eIt != theElements.end(); ++eIt )
8951     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8952
8953   // complete theElements with elements of lower dim whose all nodes are in allNodes
8954
8955   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8956   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8957   TIDSortedNodeSet::iterator nIt = allNodes.begin();
8958   for ( ; nIt != allNodes.end(); ++nIt )
8959   {
8960     const SMDS_MeshNode* n = *nIt;
8961     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8962     while ( invIt->more() )
8963     {
8964       const SMDS_MeshElement*      e = invIt->next();
8965       const SMDSAbs_ElementType type = e->GetType();
8966       if ( e->IsQuadratic() )
8967       {
8968         quadAdjacentElems[ type ].insert( e );
8969
8970         bool alreadyOK;
8971         switch ( e->GetEntityType() ) {
8972         case SMDSEntity_Quad_Triangle:
8973         case SMDSEntity_Quad_Quadrangle:
8974         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
8975         case SMDSEntity_BiQuad_Triangle:
8976         case SMDSEntity_BiQuad_Quadrangle:
8977         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
8978         default:                           alreadyOK = true;
8979         }
8980         if ( alreadyOK )
8981           continue;
8982       }
8983       if ( type >= elemType )
8984         continue; // same type or more complex linear element
8985
8986       if ( !checkedAdjacentElems[ type ].insert( e ).second )
8987         continue; // e is already checked
8988
8989       // check nodes
8990       bool allIn = true;
8991       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
8992       while ( nodeIt->more() && allIn )
8993         allIn = allNodes.count( nodeIt->next() );
8994       if ( allIn )
8995         theElements.insert(e );
8996     }
8997   }
8998
8999   SMESH_MesherHelper helper(*myMesh);
9000   helper.SetIsQuadratic( true );
9001   helper.SetIsBiQuadratic( theToBiQuad );
9002
9003   // add links of quadratic adjacent elements to the helper
9004
9005   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9006     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9007           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9008     {
9009       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9010     }
9011   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9012     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9013           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9014     {
9015       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9016     }
9017   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9018     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9019           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9020     {
9021       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9022     }
9023
9024   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9025
9026   SMESHDS_Mesh*  meshDS = GetMeshDS();
9027   SMESHDS_SubMesh* smDS = 0;
9028   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9029   {
9030     const SMDS_MeshElement* elem = *eIt;
9031
9032     bool alreadyOK;
9033     int nbCentralNodes = 0;
9034     switch ( elem->GetEntityType() ) {
9035       // linear convertible
9036     case SMDSEntity_Edge:
9037     case SMDSEntity_Triangle:
9038     case SMDSEntity_Quadrangle:
9039     case SMDSEntity_Tetra:
9040     case SMDSEntity_Pyramid:
9041     case SMDSEntity_Hexa:
9042     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9043       // quadratic that can become bi-quadratic
9044     case SMDSEntity_Quad_Triangle:
9045     case SMDSEntity_Quad_Quadrangle:
9046     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9047       // bi-quadratic
9048     case SMDSEntity_BiQuad_Triangle:
9049     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9050     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9051       // the rest
9052     default:                           alreadyOK = true;
9053     }
9054     if ( alreadyOK ) continue;
9055
9056     const SMDSAbs_ElementType type = elem->GetType();
9057     const int                   id = elem->GetID();
9058     const int              nbNodes = elem->NbCornerNodes();
9059     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9060
9061     helper.SetSubShape( elem->getshapeId() );
9062
9063     if ( !smDS || !smDS->Contains( elem ))
9064       smDS = meshDS->MeshElements( elem->getshapeId() );
9065     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9066
9067     SMDS_MeshElement * newElem = 0;
9068     switch( nbNodes )
9069     {
9070     case 4: // cases for most frequently used element types go first (for optimization)
9071       if ( type == SMDSAbs_Volume )
9072         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9073       else
9074         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9075       break;
9076     case 8:
9077       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9078                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9079       break;
9080     case 3:
9081       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9082       break;
9083     case 2:
9084       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9085       break;
9086     case 5:
9087       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9088                                  nodes[4], id, theForce3d);
9089       break;
9090     case 6:
9091       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9092                                  nodes[4], nodes[5], id, theForce3d);
9093       break;
9094     default:;
9095     }
9096     ReplaceElemInGroups( elem, newElem, meshDS);
9097     if( newElem && smDS )
9098       smDS->AddElement( newElem );
9099
9100      // remove central nodes
9101     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9102       if ( nodes[i]->NbInverseElements() == 0 )
9103         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9104
9105   } // loop on theElements
9106
9107   if ( !theForce3d )
9108   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9109     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9110     // helper.FixQuadraticElements( myError );
9111     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9112   }
9113 }
9114
9115 //=======================================================================
9116 /*!
9117  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9118  * \return int - nb of checked elements
9119  */
9120 //=======================================================================
9121
9122 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9123                                      SMDS_ElemIteratorPtr theItr,
9124                                      const int            theShapeID)
9125 {
9126   int nbElem = 0;
9127   SMESHDS_Mesh* meshDS = GetMeshDS();
9128
9129   while( theItr->more() )
9130   {
9131     const SMDS_MeshElement* elem = theItr->next();
9132     nbElem++;
9133     if( elem && elem->IsQuadratic())
9134     {
9135       int id                    = elem->GetID();
9136       int nbCornerNodes         = elem->NbCornerNodes();
9137       SMDSAbs_ElementType aType = elem->GetType();
9138
9139       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9140
9141       //remove a quadratic element
9142       if ( !theSm || !theSm->Contains( elem ))
9143         theSm = meshDS->MeshElements( elem->getshapeId() );
9144       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9145
9146       // remove medium nodes
9147       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9148         if ( nodes[i]->NbInverseElements() == 0 )
9149           meshDS->RemoveFreeNode( nodes[i], theSm );
9150
9151       // add a linear element
9152       nodes.resize( nbCornerNodes );
9153       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9154       ReplaceElemInGroups(elem, newElem, meshDS);
9155       if( theSm && newElem )
9156         theSm->AddElement( newElem );
9157     }
9158   }
9159   return nbElem;
9160 }
9161
9162 //=======================================================================
9163 //function : ConvertFromQuadratic
9164 //purpose  :
9165 //=======================================================================
9166
9167 bool SMESH_MeshEditor::ConvertFromQuadratic()
9168 {
9169   int nbCheckedElems = 0;
9170   if ( myMesh->HasShapeToMesh() )
9171   {
9172     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9173     {
9174       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9175       while ( smIt->more() ) {
9176         SMESH_subMesh* sm = smIt->next();
9177         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9178           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9179       }
9180     }
9181   }
9182
9183   int totalNbElems =
9184     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9185   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9186   {
9187     SMESHDS_SubMesh *aSM = 0;
9188     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9189   }
9190
9191   return true;
9192 }
9193
9194 namespace
9195 {
9196   //================================================================================
9197   /*!
9198    * \brief Return true if all medium nodes of the element are in the node set
9199    */
9200   //================================================================================
9201
9202   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9203   {
9204     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9205       if ( !nodeSet.count( elem->GetNode(i) ))
9206         return false;
9207     return true;
9208   }
9209 }
9210
9211 //================================================================================
9212 /*!
9213  * \brief Makes given elements linear
9214  */
9215 //================================================================================
9216
9217 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9218 {
9219   if ( theElements.empty() ) return;
9220
9221   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9222   set<int> mediumNodeIDs;
9223   TIDSortedElemSet::iterator eIt = theElements.begin();
9224   for ( ; eIt != theElements.end(); ++eIt )
9225   {
9226     const SMDS_MeshElement* e = *eIt;
9227     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9228       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9229   }
9230
9231   // replace given elements by linear ones
9232   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9233   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9234
9235   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9236   // except those elements sharing medium nodes of quadratic element whose medium nodes
9237   // are not all in mediumNodeIDs
9238
9239   // get remaining medium nodes
9240   TIDSortedNodeSet mediumNodes;
9241   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9242   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9243     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9244       mediumNodes.insert( mediumNodes.end(), n );
9245
9246   // find more quadratic elements to convert
9247   TIDSortedElemSet moreElemsToConvert;
9248   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9249   for ( ; nIt != mediumNodes.end(); ++nIt )
9250   {
9251     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9252     while ( invIt->more() )
9253     {
9254       const SMDS_MeshElement* e = invIt->next();
9255       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9256       {
9257         // find a more complex element including e and
9258         // whose medium nodes are not in mediumNodes
9259         bool complexFound = false;
9260         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9261         {
9262           SMDS_ElemIteratorPtr invIt2 =
9263             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9264           while ( invIt2->more() )
9265           {
9266             const SMDS_MeshElement* eComplex = invIt2->next();
9267             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9268             {
9269               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9270               if ( nbCommonNodes == e->NbNodes())
9271               {
9272                 complexFound = true;
9273                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9274                 break;
9275               }
9276             }
9277           }
9278         }
9279         if ( !complexFound )
9280           moreElemsToConvert.insert( e );
9281       }
9282     }
9283   }
9284   elemIt = elemSetIterator( moreElemsToConvert );
9285   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9286 }
9287
9288 //=======================================================================
9289 //function : SewSideElements
9290 //purpose  :
9291 //=======================================================================
9292
9293 SMESH_MeshEditor::Sew_Error
9294 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9295                                    TIDSortedElemSet&    theSide2,
9296                                    const SMDS_MeshNode* theFirstNode1,
9297                                    const SMDS_MeshNode* theFirstNode2,
9298                                    const SMDS_MeshNode* theSecondNode1,
9299                                    const SMDS_MeshNode* theSecondNode2)
9300 {
9301   myLastCreatedElems.Clear();
9302   myLastCreatedNodes.Clear();
9303
9304   MESSAGE ("::::SewSideElements()");
9305   if ( theSide1.size() != theSide2.size() )
9306     return SEW_DIFF_NB_OF_ELEMENTS;
9307
9308   Sew_Error aResult = SEW_OK;
9309   // Algo:
9310   // 1. Build set of faces representing each side
9311   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9312   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9313
9314   // =======================================================================
9315   // 1. Build set of faces representing each side:
9316   // =======================================================================
9317   // a. build set of nodes belonging to faces
9318   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9319   // c. create temporary faces representing side of volumes if correspondent
9320   //    face does not exist
9321
9322   SMESHDS_Mesh* aMesh = GetMeshDS();
9323   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9324   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9325   TIDSortedElemSet             faceSet1, faceSet2;
9326   set<const SMDS_MeshElement*> volSet1,  volSet2;
9327   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9328   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9329   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9330   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9331   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9332   int iSide, iFace, iNode;
9333
9334   list<const SMDS_MeshElement* > tempFaceList;
9335   for ( iSide = 0; iSide < 2; iSide++ ) {
9336     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9337     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9338     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9339     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9340     set<const SMDS_MeshElement*>::iterator vIt;
9341     TIDSortedElemSet::iterator eIt;
9342     set<const SMDS_MeshNode*>::iterator    nIt;
9343
9344     // check that given nodes belong to given elements
9345     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9346     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9347     int firstIndex = -1, secondIndex = -1;
9348     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9349       const SMDS_MeshElement* elem = *eIt;
9350       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9351       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9352       if ( firstIndex > -1 && secondIndex > -1 ) break;
9353     }
9354     if ( firstIndex < 0 || secondIndex < 0 ) {
9355       // we can simply return until temporary faces created
9356       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9357     }
9358
9359     // -----------------------------------------------------------
9360     // 1a. Collect nodes of existing faces
9361     //     and build set of face nodes in order to detect missing
9362     //     faces corresponding to sides of volumes
9363     // -----------------------------------------------------------
9364
9365     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9366
9367     // loop on the given element of a side
9368     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9369       //const SMDS_MeshElement* elem = *eIt;
9370       const SMDS_MeshElement* elem = *eIt;
9371       if ( elem->GetType() == SMDSAbs_Face ) {
9372         faceSet->insert( elem );
9373         set <const SMDS_MeshNode*> faceNodeSet;
9374         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9375         while ( nodeIt->more() ) {
9376           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9377           nodeSet->insert( n );
9378           faceNodeSet.insert( n );
9379         }
9380         setOfFaceNodeSet.insert( faceNodeSet );
9381       }
9382       else if ( elem->GetType() == SMDSAbs_Volume )
9383         volSet->insert( elem );
9384     }
9385     // ------------------------------------------------------------------------------
9386     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9387     // ------------------------------------------------------------------------------
9388
9389     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9390       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9391       while ( fIt->more() ) { // loop on faces sharing a node
9392         const SMDS_MeshElement* f = fIt->next();
9393         if ( faceSet->find( f ) == faceSet->end() ) {
9394           // check if all nodes are in nodeSet and
9395           // complete setOfFaceNodeSet if they are
9396           set <const SMDS_MeshNode*> faceNodeSet;
9397           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9398           bool allInSet = true;
9399           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9400             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9401             if ( nodeSet->find( n ) == nodeSet->end() )
9402               allInSet = false;
9403             else
9404               faceNodeSet.insert( n );
9405           }
9406           if ( allInSet ) {
9407             faceSet->insert( f );
9408             setOfFaceNodeSet.insert( faceNodeSet );
9409           }
9410         }
9411       }
9412     }
9413
9414     // -------------------------------------------------------------------------
9415     // 1c. Create temporary faces representing sides of volumes if correspondent
9416     //     face does not exist
9417     // -------------------------------------------------------------------------
9418
9419     if ( !volSet->empty() ) {
9420       //int nodeSetSize = nodeSet->size();
9421
9422       // loop on given volumes
9423       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9424         SMDS_VolumeTool vol (*vIt);
9425         // loop on volume faces: find free faces
9426         // --------------------------------------
9427         list<const SMDS_MeshElement* > freeFaceList;
9428         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9429           if ( !vol.IsFreeFace( iFace ))
9430             continue;
9431           // check if there is already a face with same nodes in a face set
9432           const SMDS_MeshElement* aFreeFace = 0;
9433           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9434           int nbNodes = vol.NbFaceNodes( iFace );
9435           set <const SMDS_MeshNode*> faceNodeSet;
9436           vol.GetFaceNodes( iFace, faceNodeSet );
9437           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9438           if ( isNewFace ) {
9439             // no such a face is given but it still can exist, check it
9440             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9441             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9442           }
9443           if ( !aFreeFace ) {
9444             // create a temporary face
9445             if ( nbNodes == 3 ) {
9446               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9447               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9448             }
9449             else if ( nbNodes == 4 ) {
9450               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9451               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9452             }
9453             else {
9454               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9455               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9456               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9457             }
9458             if ( aFreeFace )
9459               tempFaceList.push_back( aFreeFace );
9460           }
9461
9462           if ( aFreeFace )
9463             freeFaceList.push_back( aFreeFace );
9464
9465         } // loop on faces of a volume
9466
9467         // choose one of several free faces of a volume
9468         // --------------------------------------------
9469         if ( freeFaceList.size() > 1 ) {
9470           // choose a face having max nb of nodes shared by other elems of a side
9471           int maxNbNodes = -1;
9472           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9473           while ( fIt != freeFaceList.end() ) { // loop on free faces
9474             int nbSharedNodes = 0;
9475             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9476             while ( nodeIt->more() ) { // loop on free face nodes
9477               const SMDS_MeshNode* n =
9478                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9479               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9480               while ( invElemIt->more() ) {
9481                 const SMDS_MeshElement* e = invElemIt->next();
9482                 nbSharedNodes += faceSet->count( e );
9483                 nbSharedNodes += elemSet->count( e );
9484               }
9485             }
9486             if ( nbSharedNodes > maxNbNodes ) {
9487               maxNbNodes = nbSharedNodes;
9488               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9489             }
9490             else if ( nbSharedNodes == maxNbNodes ) {
9491               fIt++;
9492             }
9493             else {
9494               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9495             }
9496           }
9497           if ( freeFaceList.size() > 1 )
9498           {
9499             // could not choose one face, use another way
9500             // choose a face most close to the bary center of the opposite side
9501             gp_XYZ aBC( 0., 0., 0. );
9502             set <const SMDS_MeshNode*> addedNodes;
9503             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9504             eIt = elemSet2->begin();
9505             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9506               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9507               while ( nodeIt->more() ) { // loop on free face nodes
9508                 const SMDS_MeshNode* n =
9509                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9510                 if ( addedNodes.insert( n ).second )
9511                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9512               }
9513             }
9514             aBC /= addedNodes.size();
9515             double minDist = DBL_MAX;
9516             fIt = freeFaceList.begin();
9517             while ( fIt != freeFaceList.end() ) { // loop on free faces
9518               double dist = 0;
9519               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9520               while ( nodeIt->more() ) { // loop on free face nodes
9521                 const SMDS_MeshNode* n =
9522                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9523                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9524                 dist += ( aBC - p ).SquareModulus();
9525               }
9526               if ( dist < minDist ) {
9527                 minDist = dist;
9528                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9529               }
9530               else
9531                 fIt = freeFaceList.erase( fIt++ );
9532             }
9533           }
9534         } // choose one of several free faces of a volume
9535
9536         if ( freeFaceList.size() == 1 ) {
9537           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9538           faceSet->insert( aFreeFace );
9539           // complete a node set with nodes of a found free face
9540           //           for ( iNode = 0; iNode < ; iNode++ )
9541           //             nodeSet->insert( fNodes[ iNode ] );
9542         }
9543
9544       } // loop on volumes of a side
9545
9546       //       // complete a set of faces if new nodes in a nodeSet appeared
9547       //       // ----------------------------------------------------------
9548       //       if ( nodeSetSize != nodeSet->size() ) {
9549       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9550       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9551       //           while ( fIt->more() ) { // loop on faces sharing a node
9552       //             const SMDS_MeshElement* f = fIt->next();
9553       //             if ( faceSet->find( f ) == faceSet->end() ) {
9554       //               // check if all nodes are in nodeSet and
9555       //               // complete setOfFaceNodeSet if they are
9556       //               set <const SMDS_MeshNode*> faceNodeSet;
9557       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9558       //               bool allInSet = true;
9559       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9560       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9561       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9562       //                   allInSet = false;
9563       //                 else
9564       //                   faceNodeSet.insert( n );
9565       //               }
9566       //               if ( allInSet ) {
9567       //                 faceSet->insert( f );
9568       //                 setOfFaceNodeSet.insert( faceNodeSet );
9569       //               }
9570       //             }
9571       //           }
9572       //         }
9573       //       }
9574     } // Create temporary faces, if there are volumes given
9575   } // loop on sides
9576
9577   if ( faceSet1.size() != faceSet2.size() ) {
9578     // delete temporary faces: they are in reverseElements of actual nodes
9579 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9580 //    while ( tmpFaceIt->more() )
9581 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9582 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9583 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9584 //      aMesh->RemoveElement(*tmpFaceIt);
9585     MESSAGE("Diff nb of faces");
9586     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9587   }
9588
9589   // ============================================================
9590   // 2. Find nodes to merge:
9591   //              bind a node to remove to a node to put instead
9592   // ============================================================
9593
9594   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9595   if ( theFirstNode1 != theFirstNode2 )
9596     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9597   if ( theSecondNode1 != theSecondNode2 )
9598     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9599
9600   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9601   set< long > linkIdSet; // links to process
9602   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9603
9604   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9605   list< NLink > linkList[2];
9606   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9607   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9608   // loop on links in linkList; find faces by links and append links
9609   // of the found faces to linkList
9610   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9611   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9612   {
9613     NLink link[] = { *linkIt[0], *linkIt[1] };
9614     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9615     if ( !linkIdSet.count( linkID ) )
9616       continue;
9617
9618     // by links, find faces in the face sets,
9619     // and find indices of link nodes in the found faces;
9620     // in a face set, there is only one or no face sharing a link
9621     // ---------------------------------------------------------------
9622
9623     const SMDS_MeshElement* face[] = { 0, 0 };
9624     vector<const SMDS_MeshNode*> fnodes[2];
9625     int iLinkNode[2][2];
9626     TIDSortedElemSet avoidSet;
9627     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9628       const SMDS_MeshNode* n1 = link[iSide].first;
9629       const SMDS_MeshNode* n2 = link[iSide].second;
9630       //cout << "Side " << iSide << " ";
9631       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9632       // find a face by two link nodes
9633       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9634                                                       *faceSetPtr[ iSide ], avoidSet,
9635                                                       &iLinkNode[iSide][0],
9636                                                       &iLinkNode[iSide][1] );
9637       if ( face[ iSide ])
9638       {
9639         //cout << " F " << face[ iSide]->GetID() <<endl;
9640         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9641         // put face nodes to fnodes
9642         if ( face[ iSide ]->IsQuadratic() )
9643         {
9644           // use interlaced nodes iterator
9645           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9646           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9647           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9648           while ( nIter->more() )
9649             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9650         }
9651         else
9652         {
9653           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9654                                   face[ iSide ]->end_nodes() );
9655         }
9656         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9657       }
9658     }
9659
9660     // check similarity of elements of the sides
9661     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9662       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9663       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9664         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9665       }
9666       else {
9667         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9668       }
9669       break; // do not return because it's necessary to remove tmp faces
9670     }
9671
9672     // set nodes to merge
9673     // -------------------
9674
9675     if ( face[0] && face[1] )  {
9676       const int nbNodes = face[0]->NbNodes();
9677       if ( nbNodes != face[1]->NbNodes() ) {
9678         MESSAGE("Diff nb of face nodes");
9679         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9680         break; // do not return because it s necessary to remove tmp faces
9681       }
9682       bool reverse[] = { false, false }; // order of nodes in the link
9683       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9684         // analyse link orientation in faces
9685         int i1 = iLinkNode[ iSide ][ 0 ];
9686         int i2 = iLinkNode[ iSide ][ 1 ];
9687         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9688       }
9689       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9690       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9691       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9692       {
9693         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9694                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9695       }
9696
9697       // add other links of the faces to linkList
9698       // -----------------------------------------
9699
9700       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9701         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9702         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9703         if ( !iter_isnew.second ) { // already in a set: no need to process
9704           linkIdSet.erase( iter_isnew.first );
9705         }
9706         else // new in set == encountered for the first time: add
9707         {
9708           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9709           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9710           linkList[0].push_back ( NLink( n1, n2 ));
9711           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9712         }
9713       }
9714     } // 2 faces found
9715
9716     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9717       break;
9718
9719   } // loop on link lists
9720
9721   if ( aResult == SEW_OK &&
9722        ( //linkIt[0] != linkList[0].end() ||
9723          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9724     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9725              " " << (faceSetPtr[1]->empty()));
9726     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9727   }
9728
9729   // ====================================================================
9730   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9731   // ====================================================================
9732
9733   // delete temporary faces
9734 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9735 //  while ( tmpFaceIt->more() )
9736 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9737   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9738   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9739     aMesh->RemoveElement(*tmpFaceIt);
9740
9741   if ( aResult != SEW_OK)
9742     return aResult;
9743
9744   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9745   // loop on nodes replacement map
9746   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9747   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9748     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9749       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9750       nodeIDsToRemove.push_back( nToRemove->GetID() );
9751       // loop on elements sharing nToRemove
9752       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9753       while ( invElemIt->more() ) {
9754         const SMDS_MeshElement* e = invElemIt->next();
9755         // get a new suite of nodes: make replacement
9756         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9757         vector< const SMDS_MeshNode*> nodes( nbNodes );
9758         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9759         while ( nIt->more() ) {
9760           const SMDS_MeshNode* n =
9761             static_cast<const SMDS_MeshNode*>( nIt->next() );
9762           nnIt = nReplaceMap.find( n );
9763           if ( nnIt != nReplaceMap.end() ) {
9764             nbReplaced++;
9765             n = (*nnIt).second;
9766           }
9767           nodes[ i++ ] = n;
9768         }
9769         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9770         //         elemIDsToRemove.push_back( e->GetID() );
9771         //       else
9772         if ( nbReplaced )
9773           {
9774             SMDSAbs_ElementType etyp = e->GetType();
9775             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9776             if (newElem)
9777               {
9778                 myLastCreatedElems.Append(newElem);
9779                 AddToSameGroups(newElem, e, aMesh);
9780                 int aShapeId = e->getshapeId();
9781                 if ( aShapeId )
9782                   {
9783                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
9784                   }
9785               }
9786             aMesh->RemoveElement(e);
9787           }
9788       }
9789     }
9790
9791   Remove( nodeIDsToRemove, true );
9792
9793   return aResult;
9794 }
9795
9796 //================================================================================
9797 /*!
9798  * \brief Find corresponding nodes in two sets of faces
9799  * \param theSide1 - first face set
9800  * \param theSide2 - second first face
9801  * \param theFirstNode1 - a boundary node of set 1
9802  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9803  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9804  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9805  * \param nReplaceMap - output map of corresponding nodes
9806  * \return bool  - is a success or not
9807  */
9808 //================================================================================
9809
9810 #ifdef _DEBUG_
9811 //#define DEBUG_MATCHING_NODES
9812 #endif
9813
9814 SMESH_MeshEditor::Sew_Error
9815 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9816                                     set<const SMDS_MeshElement*>& theSide2,
9817                                     const SMDS_MeshNode*          theFirstNode1,
9818                                     const SMDS_MeshNode*          theFirstNode2,
9819                                     const SMDS_MeshNode*          theSecondNode1,
9820                                     const SMDS_MeshNode*          theSecondNode2,
9821                                     TNodeNodeMap &                nReplaceMap)
9822 {
9823   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9824
9825   nReplaceMap.clear();
9826   if ( theFirstNode1 != theFirstNode2 )
9827     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9828   if ( theSecondNode1 != theSecondNode2 )
9829     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9830
9831   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9832   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9833
9834   list< NLink > linkList[2];
9835   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9836   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9837
9838   // loop on links in linkList; find faces by links and append links
9839   // of the found faces to linkList
9840   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9841   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9842     NLink link[] = { *linkIt[0], *linkIt[1] };
9843     if ( linkSet.find( link[0] ) == linkSet.end() )
9844       continue;
9845
9846     // by links, find faces in the face sets,
9847     // and find indices of link nodes in the found faces;
9848     // in a face set, there is only one or no face sharing a link
9849     // ---------------------------------------------------------------
9850
9851     const SMDS_MeshElement* face[] = { 0, 0 };
9852     list<const SMDS_MeshNode*> notLinkNodes[2];
9853     //bool reverse[] = { false, false }; // order of notLinkNodes
9854     int nbNodes[2];
9855     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9856     {
9857       const SMDS_MeshNode* n1 = link[iSide].first;
9858       const SMDS_MeshNode* n2 = link[iSide].second;
9859       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9860       set< const SMDS_MeshElement* > facesOfNode1;
9861       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9862       {
9863         // during a loop of the first node, we find all faces around n1,
9864         // during a loop of the second node, we find one face sharing both n1 and n2
9865         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9866         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9867         while ( fIt->more() ) { // loop on faces sharing a node
9868           const SMDS_MeshElement* f = fIt->next();
9869           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9870               ! facesOfNode1.insert( f ).second ) // f encounters twice
9871           {
9872             if ( face[ iSide ] ) {
9873               MESSAGE( "2 faces per link " );
9874               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9875             }
9876             face[ iSide ] = f;
9877             faceSet->erase( f );
9878
9879             // get not link nodes
9880             int nbN = f->NbNodes();
9881             if ( f->IsQuadratic() )
9882               nbN /= 2;
9883             nbNodes[ iSide ] = nbN;
9884             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9885             int i1 = f->GetNodeIndex( n1 );
9886             int i2 = f->GetNodeIndex( n2 );
9887             int iEnd = nbN, iBeg = -1, iDelta = 1;
9888             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9889             if ( reverse ) {
9890               std::swap( iEnd, iBeg ); iDelta = -1;
9891             }
9892             int i = i2;
9893             while ( true ) {
9894               i += iDelta;
9895               if ( i == iEnd ) i = iBeg + iDelta;
9896               if ( i == i1 ) break;
9897               nodes.push_back ( f->GetNode( i ) );
9898             }
9899           }
9900         }
9901       }
9902     }
9903     // check similarity of elements of the sides
9904     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9905       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9906       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9907         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9908       }
9909       else {
9910         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9911       }
9912     }
9913
9914     // set nodes to merge
9915     // -------------------
9916
9917     if ( face[0] && face[1] )  {
9918       if ( nbNodes[0] != nbNodes[1] ) {
9919         MESSAGE("Diff nb of face nodes");
9920         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9921       }
9922 #ifdef DEBUG_MATCHING_NODES
9923       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9924                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9925                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9926 #endif
9927       int nbN = nbNodes[0];
9928       {
9929         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9930         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9931         for ( int i = 0 ; i < nbN - 2; ++i ) {
9932 #ifdef DEBUG_MATCHING_NODES
9933           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9934 #endif
9935           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9936         }
9937       }
9938
9939       // add other links of the face 1 to linkList
9940       // -----------------------------------------
9941
9942       const SMDS_MeshElement* f0 = face[0];
9943       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9944       for ( int i = 0; i < nbN; i++ )
9945       {
9946         const SMDS_MeshNode* n2 = f0->GetNode( i );
9947         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9948           linkSet.insert( SMESH_TLink( n1, n2 ));
9949         if ( !iter_isnew.second ) { // already in a set: no need to process
9950           linkSet.erase( iter_isnew.first );
9951         }
9952         else // new in set == encountered for the first time: add
9953         {
9954 #ifdef DEBUG_MATCHING_NODES
9955           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9956                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9957 #endif
9958           linkList[0].push_back ( NLink( n1, n2 ));
9959           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9960         }
9961         n1 = n2;
9962       }
9963     } // 2 faces found
9964   } // loop on link lists
9965
9966   return SEW_OK;
9967 }
9968
9969 //================================================================================
9970 /*!
9971  * \brief Create elements equal (on same nodes) to given ones
9972  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
9973  *              elements of the uppest dimension are duplicated.
9974  */
9975 //================================================================================
9976
9977 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9978 {
9979   CrearLastCreated();
9980   SMESHDS_Mesh* mesh = GetMeshDS();
9981
9982   // get an element type and an iterator over elements
9983
9984   SMDSAbs_ElementType type;
9985   SMDS_ElemIteratorPtr elemIt;
9986   vector< const SMDS_MeshElement* > allElems;
9987   if ( theElements.empty() )
9988   {
9989     if ( mesh->NbNodes() == 0 )
9990       return;
9991     // get most complex type
9992     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
9993       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
9994       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
9995     };
9996     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
9997       if ( mesh->GetMeshInfo().NbElements( types[i] ))
9998       {
9999         type = types[i];
10000         break;
10001       }
10002     // put all elements in the vector <allElems>
10003     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10004     elemIt = mesh->elementsIterator( type );
10005     while ( elemIt->more() )
10006       allElems.push_back( elemIt->next());
10007     elemIt = elemSetIterator( allElems );
10008   }
10009   else
10010   {
10011     type = (*theElements.begin())->GetType();
10012     elemIt = elemSetIterator( theElements );
10013   }
10014
10015   // duplicate elements
10016
10017   if ( type == SMDSAbs_Ball )
10018   {
10019     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10020     while ( elemIt->more() )
10021     {
10022       const SMDS_MeshElement* elem = elemIt->next();
10023       if ( elem->GetType() != SMDSAbs_Ball )
10024         continue;
10025       if (( elem = mesh->AddBall( elem->GetNode(0),
10026                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10027         myLastCreatedElems.Append( elem );
10028     }
10029   }
10030   else
10031   {
10032     vector< const SMDS_MeshNode* > nodes;
10033     while ( elemIt->more() )
10034     {
10035       const SMDS_MeshElement* elem = elemIt->next();
10036       if ( elem->GetType() != type )
10037         continue;
10038
10039       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10040
10041       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10042       {
10043         std::vector<int> quantities =
10044           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10045         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10046       }
10047       else
10048       {
10049         AddElement( nodes, type, elem->IsPoly() );
10050         elem = 0; // myLastCreatedElems is already filled
10051       }
10052       if ( elem )
10053         myLastCreatedElems.Append( elem );
10054     }
10055   }
10056 }
10057
10058 //================================================================================
10059 /*!
10060   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10061   \param theElems - the list of elements (edges or faces) to be replicated
10062   The nodes for duplication could be found from these elements
10063   \param theNodesNot - list of nodes to NOT replicate
10064   \param theAffectedElems - the list of elements (cells and edges) to which the
10065   replicated nodes should be associated to.
10066   \return TRUE if operation has been completed successfully, FALSE otherwise
10067 */
10068 //================================================================================
10069
10070 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10071                                     const TIDSortedElemSet& theNodesNot,
10072                                     const TIDSortedElemSet& theAffectedElems )
10073 {
10074   myLastCreatedElems.Clear();
10075   myLastCreatedNodes.Clear();
10076
10077   if ( theElems.size() == 0 )
10078     return false;
10079
10080   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10081   if ( !aMeshDS )
10082     return false;
10083
10084   bool res = false;
10085   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10086   // duplicate elements and nodes
10087   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10088   // replce nodes by duplications
10089   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10090   return res;
10091 }
10092
10093 //================================================================================
10094 /*!
10095   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10096   \param theMeshDS - mesh instance
10097   \param theElems - the elements replicated or modified (nodes should be changed)
10098   \param theNodesNot - nodes to NOT replicate
10099   \param theNodeNodeMap - relation of old node to new created node
10100   \param theIsDoubleElem - flag os to replicate element or modify
10101   \return TRUE if operation has been completed successfully, FALSE otherwise
10102 */
10103 //================================================================================
10104
10105 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10106                                     const TIDSortedElemSet& theElems,
10107                                     const TIDSortedElemSet& theNodesNot,
10108                                     std::map< const SMDS_MeshNode*,
10109                                     const SMDS_MeshNode* >& theNodeNodeMap,
10110                                     const bool theIsDoubleElem )
10111 {
10112   MESSAGE("doubleNodes");
10113   // iterate on through element and duplicate them (by nodes duplication)
10114   bool res = false;
10115   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10116   for ( ;  elemItr != theElems.end(); ++elemItr )
10117   {
10118     const SMDS_MeshElement* anElem = *elemItr;
10119     if (!anElem)
10120       continue;
10121
10122     bool isDuplicate = false;
10123     // duplicate nodes to duplicate element
10124     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10125     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10126     int ind = 0;
10127     while ( anIter->more() )
10128     {
10129
10130       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10131       SMDS_MeshNode* aNewNode = aCurrNode;
10132       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10133         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10134       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10135       {
10136         // duplicate node
10137         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10138         theNodeNodeMap[ aCurrNode ] = aNewNode;
10139         myLastCreatedNodes.Append( aNewNode );
10140       }
10141       isDuplicate |= (aCurrNode != aNewNode);
10142       newNodes[ ind++ ] = aNewNode;
10143     }
10144     if ( !isDuplicate )
10145       continue;
10146
10147     if ( theIsDoubleElem )
10148       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10149     else
10150       {
10151       MESSAGE("ChangeElementNodes");
10152       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10153       }
10154     res = true;
10155   }
10156   return res;
10157 }
10158
10159 //================================================================================
10160 /*!
10161   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10162   \param theNodes - identifiers of nodes to be doubled
10163   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10164          nodes. If list of element identifiers is empty then nodes are doubled but
10165          they not assigned to elements
10166   \return TRUE if operation has been completed successfully, FALSE otherwise
10167 */
10168 //================================================================================
10169
10170 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10171                                     const std::list< int >& theListOfModifiedElems )
10172 {
10173   MESSAGE("DoubleNodes");
10174   myLastCreatedElems.Clear();
10175   myLastCreatedNodes.Clear();
10176
10177   if ( theListOfNodes.size() == 0 )
10178     return false;
10179
10180   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10181   if ( !aMeshDS )
10182     return false;
10183
10184   // iterate through nodes and duplicate them
10185
10186   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10187
10188   std::list< int >::const_iterator aNodeIter;
10189   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10190   {
10191     int aCurr = *aNodeIter;
10192     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10193     if ( !aNode )
10194       continue;
10195
10196     // duplicate node
10197
10198     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10199     if ( aNewNode )
10200     {
10201       anOldNodeToNewNode[ aNode ] = aNewNode;
10202       myLastCreatedNodes.Append( aNewNode );
10203     }
10204   }
10205
10206   // Create map of new nodes for modified elements
10207
10208   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10209
10210   std::list< int >::const_iterator anElemIter;
10211   for ( anElemIter = theListOfModifiedElems.begin();
10212         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10213   {
10214     int aCurr = *anElemIter;
10215     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10216     if ( !anElem )
10217       continue;
10218
10219     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10220
10221     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10222     int ind = 0;
10223     while ( anIter->more() )
10224     {
10225       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10226       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10227       {
10228         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10229         aNodeArr[ ind++ ] = aNewNode;
10230       }
10231       else
10232         aNodeArr[ ind++ ] = aCurrNode;
10233     }
10234     anElemToNodes[ anElem ] = aNodeArr;
10235   }
10236
10237   // Change nodes of elements
10238
10239   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10240     anElemToNodesIter = anElemToNodes.begin();
10241   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10242   {
10243     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10244     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10245     if ( anElem )
10246       {
10247       MESSAGE("ChangeElementNodes");
10248       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10249       }
10250   }
10251
10252   return true;
10253 }
10254
10255 namespace {
10256
10257   //================================================================================
10258   /*!
10259   \brief Check if element located inside shape
10260   \return TRUE if IN or ON shape, FALSE otherwise
10261   */
10262   //================================================================================
10263
10264   template<class Classifier>
10265   bool isInside(const SMDS_MeshElement* theElem,
10266                 Classifier&             theClassifier,
10267                 const double            theTol)
10268   {
10269     gp_XYZ centerXYZ (0, 0, 0);
10270     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10271     while (aNodeItr->more())
10272       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10273
10274     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10275     theClassifier.Perform(aPnt, theTol);
10276     TopAbs_State aState = theClassifier.State();
10277     return (aState == TopAbs_IN || aState == TopAbs_ON );
10278   }
10279
10280   //================================================================================
10281   /*!
10282    * \brief Classifier of the 3D point on the TopoDS_Face
10283    *        with interaface suitable for isInside()
10284    */
10285   //================================================================================
10286
10287   struct _FaceClassifier
10288   {
10289     Extrema_ExtPS       _extremum;
10290     BRepAdaptor_Surface _surface;
10291     TopAbs_State        _state;
10292
10293     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10294     {
10295       _extremum.Initialize( _surface,
10296                             _surface.FirstUParameter(), _surface.LastUParameter(),
10297                             _surface.FirstVParameter(), _surface.LastVParameter(),
10298                             _surface.Tolerance(), _surface.Tolerance() );
10299     }
10300     void Perform(const gp_Pnt& aPnt, double theTol)
10301     {
10302       _state = TopAbs_OUT;
10303       _extremum.Perform(aPnt);
10304       if ( _extremum.IsDone() )
10305         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10306 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10307           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10308 #else
10309           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10310 #endif
10311     }
10312     TopAbs_State State() const
10313     {
10314       return _state;
10315     }
10316   };
10317 }
10318
10319 //================================================================================
10320 /*!
10321   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10322   This method is the first step of DoubleNodeElemGroupsInRegion.
10323   \param theElems - list of groups of elements (edges or faces) to be replicated
10324   \param theNodesNot - list of groups of nodes not to replicated
10325   \param theShape - shape to detect affected elements (element which geometric center
10326          located on or inside shape). If the shape is null, detection is done on faces orientations
10327          (select elements with a gravity center on the side given by faces normals).
10328          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10329          The replicated nodes should be associated to affected elements.
10330   \return groups of affected elements
10331   \sa DoubleNodeElemGroupsInRegion()
10332  */
10333 //================================================================================
10334
10335 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10336                                                    const TIDSortedElemSet& theNodesNot,
10337                                                    const TopoDS_Shape&     theShape,
10338                                                    TIDSortedElemSet&       theAffectedElems)
10339 {
10340   if ( theShape.IsNull() )
10341   {
10342     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10343     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10344     std::set<const SMDS_MeshElement*> edgesToCheck;
10345     alreadyCheckedNodes.clear();
10346     alreadyCheckedElems.clear();
10347     edgesToCheck.clear();
10348
10349     // --- iterates on elements to be replicated and get elements by back references from their nodes
10350
10351     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10352     int ielem;
10353     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10354     {
10355       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10356       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10357         continue;
10358       gp_XYZ normal;
10359       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10360       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10361       std::set<const SMDS_MeshNode*> nodesElem;
10362       nodesElem.clear();
10363       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10364       while ( nodeItr->more() )
10365       {
10366         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10367         nodesElem.insert(aNode);
10368       }
10369       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10370       for (; nodit != nodesElem.end(); nodit++)
10371       {
10372         MESSAGE("  noeud ");
10373         const SMDS_MeshNode* aNode = *nodit;
10374         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10375           continue;
10376         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10377           continue;
10378         alreadyCheckedNodes.insert(aNode);
10379         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10380         while ( backElemItr->more() )
10381         {
10382           MESSAGE("    backelem ");
10383           const SMDS_MeshElement* curElem = backElemItr->next();
10384           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10385             continue;
10386           if (theElems.find(curElem) != theElems.end())
10387             continue;
10388           alreadyCheckedElems.insert(curElem);
10389           double x=0, y=0, z=0;
10390           int nb = 0;
10391           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10392           while ( nodeItr2->more() )
10393           {
10394             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10395             x += anotherNode->X();
10396             y += anotherNode->Y();
10397             z += anotherNode->Z();
10398             nb++;
10399           }
10400           gp_XYZ p;
10401           p.SetCoord( x/nb -aNode->X(),
10402                       y/nb -aNode->Y(),
10403                       z/nb -aNode->Z() );
10404           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10405           if (normal*p > 0)
10406           {
10407             MESSAGE("    --- inserted")
10408             theAffectedElems.insert( curElem );
10409           }
10410           else if (curElem->GetType() == SMDSAbs_Edge)
10411             edgesToCheck.insert(curElem);
10412         }
10413       }
10414     }
10415     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10416     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10417     for( ; eit != edgesToCheck.end(); eit++)
10418     {
10419       bool onside = true;
10420       const SMDS_MeshElement* anEdge = *eit;
10421       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10422       while ( nodeItr->more() )
10423       {
10424         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10425         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10426         {
10427           onside = false;
10428           break;
10429         }
10430       }
10431       if (onside)
10432       {
10433         MESSAGE("    --- edge onside inserted")
10434         theAffectedElems.insert(anEdge);
10435       }
10436     }
10437   }
10438   else
10439   {
10440     const double aTol = Precision::Confusion();
10441     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10442     auto_ptr<_FaceClassifier>              aFaceClassifier;
10443     if ( theShape.ShapeType() == TopAbs_SOLID )
10444     {
10445       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10446       bsc3d->PerformInfinitePoint(aTol);
10447     }
10448     else if (theShape.ShapeType() == TopAbs_FACE )
10449     {
10450       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10451     }
10452
10453     // iterates on indicated elements and get elements by back references from their nodes
10454     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10455     int ielem;
10456     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10457     {
10458       MESSAGE("element " << ielem++);
10459       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10460       if (!anElem)
10461         continue;
10462       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10463       while ( nodeItr->more() )
10464       {
10465         MESSAGE("  noeud ");
10466         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10467         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10468           continue;
10469         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10470         while ( backElemItr->more() )
10471         {
10472           MESSAGE("    backelem ");
10473           const SMDS_MeshElement* curElem = backElemItr->next();
10474           if ( curElem && theElems.find(curElem) == theElems.end() &&
10475               ( bsc3d.get() ?
10476                 isInside( curElem, *bsc3d, aTol ) :
10477                 isInside( curElem, *aFaceClassifier, aTol )))
10478             theAffectedElems.insert( curElem );
10479         }
10480       }
10481     }
10482   }
10483   return true;
10484 }
10485
10486 //================================================================================
10487 /*!
10488   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10489   \param theElems - group of of elements (edges or faces) to be replicated
10490   \param theNodesNot - group of nodes not to replicate
10491   \param theShape - shape to detect affected elements (element which geometric center
10492   located on or inside shape).
10493   The replicated nodes should be associated to affected elements.
10494   \return TRUE if operation has been completed successfully, FALSE otherwise
10495 */
10496 //================================================================================
10497
10498 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10499                                             const TIDSortedElemSet& theNodesNot,
10500                                             const TopoDS_Shape&     theShape )
10501 {
10502   if ( theShape.IsNull() )
10503     return false;
10504
10505   const double aTol = Precision::Confusion();
10506   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10507   auto_ptr<_FaceClassifier>              aFaceClassifier;
10508   if ( theShape.ShapeType() == TopAbs_SOLID )
10509   {
10510     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10511     bsc3d->PerformInfinitePoint(aTol);
10512   }
10513   else if (theShape.ShapeType() == TopAbs_FACE )
10514   {
10515     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10516   }
10517
10518   // iterates on indicated elements and get elements by back references from their nodes
10519   TIDSortedElemSet anAffected;
10520   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10521   for ( ;  elemItr != theElems.end(); ++elemItr )
10522   {
10523     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10524     if (!anElem)
10525       continue;
10526
10527     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10528     while ( nodeItr->more() )
10529     {
10530       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10531       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10532         continue;
10533       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10534       while ( backElemItr->more() )
10535       {
10536         const SMDS_MeshElement* curElem = backElemItr->next();
10537         if ( curElem && theElems.find(curElem) == theElems.end() &&
10538              ( bsc3d.get() ?
10539                isInside( curElem, *bsc3d, aTol ) :
10540                isInside( curElem, *aFaceClassifier, aTol )))
10541           anAffected.insert( curElem );
10542       }
10543     }
10544   }
10545   return DoubleNodes( theElems, theNodesNot, anAffected );
10546 }
10547
10548 /*!
10549  *  \brief compute an oriented angle between two planes defined by four points.
10550  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10551  *  @param p0 base of the rotation axe
10552  *  @param p1 extremity of the rotation axe
10553  *  @param g1 belongs to the first plane
10554  *  @param g2 belongs to the second plane
10555  */
10556 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10557 {
10558 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10559 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10560 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10561 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10562   gp_Vec vref(p0, p1);
10563   gp_Vec v1(p0, g1);
10564   gp_Vec v2(p0, g2);
10565   gp_Vec n1 = vref.Crossed(v1);
10566   gp_Vec n2 = vref.Crossed(v2);
10567   try {
10568     return n2.AngleWithRef(n1, vref);
10569   }
10570   catch ( Standard_Failure ) {
10571   }
10572   return Max( v1.Magnitude(), v2.Magnitude() );
10573 }
10574
10575 /*!
10576  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10577  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10578  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10579  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10580  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10581  * 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.
10582  * 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.
10583  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10584  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10585  * \param theElems - list of groups of volumes, where a group of volume is a set of
10586  *        SMDS_MeshElements sorted by Id.
10587  * \param createJointElems - if TRUE, create the elements
10588  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10589  *        the boundary between \a theDomains and the rest mesh
10590  * \return TRUE if operation has been completed successfully, FALSE otherwise
10591  */
10592 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10593                                                      bool                                 createJointElems,
10594                                                      bool                                 onAllBoundaries)
10595 {
10596   MESSAGE("----------------------------------------------");
10597   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10598   MESSAGE("----------------------------------------------");
10599
10600   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10601   meshDS->BuildDownWardConnectivity(true);
10602   CHRONO(50);
10603   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10604
10605   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10606   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10607   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10608
10609   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10610   std::map<int,int>celldom; // cell vtkId --> domain
10611   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10612   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10613   faceDomains.clear();
10614   celldom.clear();
10615   cellDomains.clear();
10616   nodeDomains.clear();
10617   std::map<int,int> emptyMap;
10618   std::set<int> emptySet;
10619   emptyMap.clear();
10620
10621   MESSAGE(".. Number of domains :"<<theElems.size());
10622
10623   TIDSortedElemSet theRestDomElems;
10624   const int iRestDom  = -1;
10625   const int idom0     = onAllBoundaries ? iRestDom : 0;
10626   const int nbDomains = theElems.size();
10627
10628   // Check if the domains do not share an element
10629   for (int idom = 0; idom < nbDomains-1; idom++)
10630     {
10631 //       MESSAGE("... Check of domain #" << idom);
10632       const TIDSortedElemSet& domain = theElems[idom];
10633       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10634       for (; elemItr != domain.end(); ++elemItr)
10635         {
10636           const SMDS_MeshElement* anElem = *elemItr;
10637           int idombisdeb = idom + 1 ;
10638           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10639           {
10640             const TIDSortedElemSet& domainbis = theElems[idombis];
10641             if ( domainbis.count(anElem) )
10642             {
10643               MESSAGE(".... Domain #" << idom);
10644               MESSAGE(".... Domain #" << idombis);
10645               throw SALOME_Exception("The domains are not disjoint.");
10646               return false ;
10647             }
10648           }
10649         }
10650     }
10651
10652   for (int idom = 0; idom < nbDomains; idom++)
10653     {
10654
10655       // --- build a map (face to duplicate --> volume to modify)
10656       //     with all the faces shared by 2 domains (group of elements)
10657       //     and corresponding volume of this domain, for each shared face.
10658       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10659
10660       MESSAGE("... Neighbors of domain #" << idom);
10661       const TIDSortedElemSet& domain = theElems[idom];
10662       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10663       for (; elemItr != domain.end(); ++elemItr)
10664         {
10665           const SMDS_MeshElement* anElem = *elemItr;
10666           if (!anElem)
10667             continue;
10668           int vtkId = anElem->getVtkId();
10669           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
10670           int neighborsVtkIds[NBMAXNEIGHBORS];
10671           int downIds[NBMAXNEIGHBORS];
10672           unsigned char downTypes[NBMAXNEIGHBORS];
10673           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10674           for (int n = 0; n < nbNeighbors; n++)
10675             {
10676               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10677               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10678               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10679                 {
10680                   bool ok = false ;
10681                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10682                   {
10683                     // MESSAGE("Domain " << idombis);
10684                     const TIDSortedElemSet& domainbis = theElems[idombis];
10685                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10686                   }
10687                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10688                   {
10689                     DownIdType face(downIds[n], downTypes[n]);
10690                     if (!faceDomains[face].count(idom))
10691                       {
10692                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10693                         celldom[vtkId] = idom;
10694                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
10695                       }
10696                     if ( !ok )
10697                     {
10698                       theRestDomElems.insert( elem );
10699                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
10700                       celldom[neighborsVtkIds[n]] = iRestDom;
10701                     }
10702                   }
10703                 }
10704             }
10705         }
10706     }
10707
10708   //MESSAGE("Number of shared faces " << faceDomains.size());
10709   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10710
10711   // --- explore the shared faces domain by domain,
10712   //     explore the nodes of the face and see if they belong to a cell in the domain,
10713   //     which has only a node or an edge on the border (not a shared face)
10714
10715   for (int idomain = idom0; idomain < nbDomains; idomain++)
10716     {
10717       //MESSAGE("Domain " << idomain);
10718       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10719       itface = faceDomains.begin();
10720       for (; itface != faceDomains.end(); ++itface)
10721         {
10722           const std::map<int, int>& domvol = itface->second;
10723           if (!domvol.count(idomain))
10724             continue;
10725           DownIdType face = itface->first;
10726           //MESSAGE(" --- face " << face.cellId);
10727           std::set<int> oldNodes;
10728           oldNodes.clear();
10729           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10730           std::set<int>::iterator itn = oldNodes.begin();
10731           for (; itn != oldNodes.end(); ++itn)
10732             {
10733               int oldId = *itn;
10734               //MESSAGE("     node " << oldId);
10735               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10736               for (int i=0; i<l.ncells; i++)
10737                 {
10738                   int vtkId = l.cells[i];
10739                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10740                   if (!domain.count(anElem))
10741                     continue;
10742                   int vtkType = grid->GetCellType(vtkId);
10743                   int downId = grid->CellIdToDownId(vtkId);
10744                   if (downId < 0)
10745                     {
10746                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10747                       continue; // not OK at this stage of the algorithm:
10748                                 //no cells created after BuildDownWardConnectivity
10749                     }
10750                   DownIdType aCell(downId, vtkType);
10751                   cellDomains[aCell][idomain] = vtkId;
10752                   celldom[vtkId] = idomain;
10753                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
10754                 }
10755             }
10756         }
10757     }
10758
10759   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10760   //     for each shared face, get the nodes
10761   //     for each node, for each domain of the face, create a clone of the node
10762
10763   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10764   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10765   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
10766
10767   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10768   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10769   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10770
10771   MESSAGE(".. Duplication of the nodes");
10772   for (int idomain = idom0; idomain < nbDomains; idomain++)
10773     {
10774       itface = faceDomains.begin();
10775       for (; itface != faceDomains.end(); ++itface)
10776         {
10777           const std::map<int, int>& domvol = itface->second;
10778           if (!domvol.count(idomain))
10779             continue;
10780           DownIdType face = itface->first;
10781           //MESSAGE(" --- face " << face.cellId);
10782           std::set<int> oldNodes;
10783           oldNodes.clear();
10784           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10785           std::set<int>::iterator itn = oldNodes.begin();
10786           for (; itn != oldNodes.end(); ++itn)
10787             {
10788               int oldId = *itn;
10789               if (nodeDomains[oldId].empty())
10790                 {
10791                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10792                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
10793                 }
10794               std::map<int, int>::const_iterator itdom = domvol.begin();
10795               for (; itdom != domvol.end(); ++itdom)
10796                 {
10797                   int idom = itdom->first;
10798                   //MESSAGE("         domain " << idom);
10799                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
10800                     {
10801                       if (nodeDomains[oldId].size() >= 2) // a multiple node
10802                         {
10803                           vector<int> orderedDoms;
10804                           //MESSAGE("multiple node " << oldId);
10805                           if (mutipleNodes.count(oldId))
10806                             orderedDoms = mutipleNodes[oldId];
10807                           else
10808                             {
10809                               map<int,int>::iterator it = nodeDomains[oldId].begin();
10810                               for (; it != nodeDomains[oldId].end(); ++it)
10811                                 orderedDoms.push_back(it->first);
10812                             }
10813                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
10814                           //stringstream txt;
10815                           //for (int i=0; i<orderedDoms.size(); i++)
10816                           //  txt << orderedDoms[i] << " ";
10817                           //MESSAGE("orderedDoms " << txt.str());
10818                           mutipleNodes[oldId] = orderedDoms;
10819                         }
10820                       double *coords = grid->GetPoint(oldId);
10821                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10822                       int newId = newNode->getVtkId();
10823                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
10824                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10825                     }
10826                 }
10827             }
10828         }
10829     }
10830
10831   MESSAGE(".. Creation of elements");
10832   for (int idomain = idom0; idomain < nbDomains; idomain++)
10833     {
10834       itface = faceDomains.begin();
10835       for (; itface != faceDomains.end(); ++itface)
10836         {
10837           std::map<int, int> domvol = itface->second;
10838           if (!domvol.count(idomain))
10839             continue;
10840           DownIdType face = itface->first;
10841           //MESSAGE(" --- face " << face.cellId);
10842           std::set<int> oldNodes;
10843           oldNodes.clear();
10844           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10845           int nbMultipleNodes = 0;
10846           std::set<int>::iterator itn = oldNodes.begin();
10847           for (; itn != oldNodes.end(); ++itn)
10848             {
10849               int oldId = *itn;
10850               if (mutipleNodes.count(oldId))
10851                 nbMultipleNodes++;
10852             }
10853           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10854             {
10855               //MESSAGE("multiple Nodes detected on a shared face");
10856               int downId = itface->first.cellId;
10857               unsigned char cellType = itface->first.cellType;
10858               // --- shared edge or shared face ?
10859               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10860                 {
10861                   int nodes[3];
10862                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10863                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10864                     if (mutipleNodes.count(nodes[i]))
10865                       if (!mutipleNodesToFace.count(nodes[i]))
10866                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10867                 }
10868               else // shared face (between two volumes)
10869                 {
10870                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10871                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10872                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10873                   for (int ie =0; ie < nbEdges; ie++)
10874                     {
10875                       int nodes[3];
10876                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10877                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10878                         {
10879                           vector<int> vn0 = mutipleNodes[nodes[0]];
10880                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10881                           vector<int> doms;
10882                           for (int i0 = 0; i0 < vn0.size(); i0++)
10883                             for (int i1 = 0; i1 < vn1.size(); i1++)
10884                               if (vn0[i0] == vn1[i1])
10885                                 doms.push_back(vn0[i0]);
10886                           if (doms.size() >2)
10887                             {
10888                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10889                               double *coords = grid->GetPoint(nodes[0]);
10890                               gp_Pnt p0(coords[0], coords[1], coords[2]);
10891                               coords = grid->GetPoint(nodes[nbNodes - 1]);
10892                               gp_Pnt p1(coords[0], coords[1], coords[2]);
10893                               gp_Pnt gref;
10894                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
10895                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10896                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10897                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10898                               for (int id=0; id < doms.size(); id++)
10899                                 {
10900                                   int idom = doms[id];
10901                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10902                                   for (int ivol=0; ivol<nbvol; ivol++)
10903                                     {
10904                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10905                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10906                                       if (domain.count(elem))
10907                                         {
10908                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10909                                           domvol[idom] = svol;
10910                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
10911                                           double values[3];
10912                                           vtkIdType npts = 0;
10913                                           vtkIdType* pts = 0;
10914                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10915                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10916                                           if (id ==0)
10917                                             {
10918                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10919                                               angleDom[idom] = 0;
10920                                             }
10921                                           else
10922                                             {
10923                                               gp_Pnt g(values[0], values[1], values[2]);
10924                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10925                                               //MESSAGE("  angle=" << angleDom[idom]);
10926                                             }
10927                                           break;
10928                                         }
10929                                     }
10930                                 }
10931                               map<double, int> sortedDom; // sort domains by angle
10932                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10933                                 sortedDom[ia->second] = ia->first;
10934                               vector<int> vnodes;
10935                               vector<int> vdom;
10936                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10937                                 {
10938                                   vdom.push_back(ib->second);
10939                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
10940                                 }
10941                               for (int ino = 0; ino < nbNodes; ino++)
10942                                 vnodes.push_back(nodes[ino]);
10943                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10944                             }
10945                         }
10946                     }
10947                 }
10948             }
10949         }
10950     }
10951
10952   // --- iterate on shared faces (volumes to modify, face to extrude)
10953   //     get node id's of the face (id SMDS = id VTK)
10954   //     create flat element with old and new nodes if requested
10955
10956   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10957   //     (domain1 X domain2) = domain1 + MAXINT*domain2
10958
10959   std::map<int, std::map<long,int> > nodeQuadDomains;
10960   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10961
10962   MESSAGE(".. Creation of elements: simple junction");
10963   if (createJointElems)
10964     {
10965       int idg;
10966       string joints2DName = "joints2D";
10967       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10968       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10969       string joints3DName = "joints3D";
10970       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10971       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10972
10973       itface = faceDomains.begin();
10974       for (; itface != faceDomains.end(); ++itface)
10975         {
10976           DownIdType face = itface->first;
10977           std::set<int> oldNodes;
10978           std::set<int>::iterator itn;
10979           oldNodes.clear();
10980           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10981
10982           std::map<int, int> domvol = itface->second;
10983           std::map<int, int>::iterator itdom = domvol.begin();
10984           int dom1 = itdom->first;
10985           int vtkVolId = itdom->second;
10986           itdom++;
10987           int dom2 = itdom->first;
10988           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10989                                                              nodeQuadDomains);
10990           stringstream grpname;
10991           grpname << "j_";
10992           if (dom1 < dom2)
10993             grpname << dom1 << "_" << dom2;
10994           else
10995             grpname << dom2 << "_" << dom1;
10996           string namegrp = grpname.str();
10997           if (!mapOfJunctionGroups.count(namegrp))
10998             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10999           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11000           if (sgrp)
11001             sgrp->Add(vol->GetID());
11002           if (vol->GetType() == SMDSAbs_Volume)
11003             joints3DGrp->Add(vol->GetID());
11004           else if (vol->GetType() == SMDSAbs_Face)
11005             joints2DGrp->Add(vol->GetID());
11006         }
11007     }
11008
11009   // --- create volumes on multiple domain intersection if requested
11010   //     iterate on mutipleNodesToFace
11011   //     iterate on edgesMultiDomains
11012
11013   MESSAGE(".. Creation of elements: multiple junction");
11014   if (createJointElems)
11015     {
11016       // --- iterate on mutipleNodesToFace
11017
11018       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11019       for (; itn != mutipleNodesToFace.end(); ++itn)
11020         {
11021           int node = itn->first;
11022           vector<int> orderDom = itn->second;
11023           vector<vtkIdType> orderedNodes;
11024           for (int idom = 0; idom <orderDom.size(); idom++)
11025             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11026             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11027
11028             stringstream grpname;
11029             grpname << "m2j_";
11030             grpname << 0 << "_" << 0;
11031             int idg;
11032             string namegrp = grpname.str();
11033             if (!mapOfJunctionGroups.count(namegrp))
11034               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11035             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11036             if (sgrp)
11037               sgrp->Add(face->GetID());
11038         }
11039
11040       // --- iterate on edgesMultiDomains
11041
11042       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11043       for (; ite != edgesMultiDomains.end(); ++ite)
11044         {
11045           vector<int> nodes = ite->first;
11046           vector<int> orderDom = ite->second;
11047           vector<vtkIdType> orderedNodes;
11048           if (nodes.size() == 2)
11049             {
11050               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11051               for (int ino=0; ino < nodes.size(); ino++)
11052                 if (orderDom.size() == 3)
11053                   for (int idom = 0; idom <orderDom.size(); idom++)
11054                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11055                 else
11056                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11057                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11058               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11059
11060               int idg;
11061               string namegrp = "jointsMultiples";
11062               if (!mapOfJunctionGroups.count(namegrp))
11063                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11064               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11065               if (sgrp)
11066                 sgrp->Add(vol->GetID());
11067             }
11068           else
11069             {
11070               INFOS("Quadratic multiple joints not implemented");
11071               // TODO quadratic nodes
11072             }
11073         }
11074     }
11075
11076   // --- list the explicit faces and edges of the mesh that need to be modified,
11077   //     i.e. faces and edges built with one or more duplicated nodes.
11078   //     associate these faces or edges to their corresponding domain.
11079   //     only the first domain found is kept when a face or edge is shared
11080
11081   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11082   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11083   faceOrEdgeDom.clear();
11084   feDom.clear();
11085
11086   MESSAGE(".. Modification of elements");
11087   for (int idomain = idom0; idomain < nbDomains; idomain++)
11088     {
11089       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11090       for (; itnod != nodeDomains.end(); ++itnod)
11091         {
11092           int oldId = itnod->first;
11093           //MESSAGE("     node " << oldId);
11094           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11095           for (int i = 0; i < l.ncells; i++)
11096             {
11097               int vtkId = l.cells[i];
11098               int vtkType = grid->GetCellType(vtkId);
11099               int downId = grid->CellIdToDownId(vtkId);
11100               if (downId < 0)
11101                 continue; // new cells: not to be modified
11102               DownIdType aCell(downId, vtkType);
11103               int volParents[1000];
11104               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11105               for (int j = 0; j < nbvol; j++)
11106                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11107                   if (!feDom.count(vtkId))
11108                     {
11109                       feDom[vtkId] = idomain;
11110                       faceOrEdgeDom[aCell] = emptyMap;
11111                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11112                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11113                       //        << " type " << vtkType << " downId " << downId);
11114                     }
11115             }
11116         }
11117     }
11118
11119   // --- iterate on shared faces (volumes to modify, face to extrude)
11120   //     get node id's of the face
11121   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11122
11123   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11124   for (int m=0; m<3; m++)
11125     {
11126       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11127       itface = (*amap).begin();
11128       for (; itface != (*amap).end(); ++itface)
11129         {
11130           DownIdType face = itface->first;
11131           std::set<int> oldNodes;
11132           std::set<int>::iterator itn;
11133           oldNodes.clear();
11134           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11135           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11136           std::map<int, int> localClonedNodeIds;
11137
11138           std::map<int, int> domvol = itface->second;
11139           std::map<int, int>::iterator itdom = domvol.begin();
11140           for (; itdom != domvol.end(); ++itdom)
11141             {
11142               int idom = itdom->first;
11143               int vtkVolId = itdom->second;
11144               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11145               localClonedNodeIds.clear();
11146               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11147                 {
11148                   int oldId = *itn;
11149                   if (nodeDomains[oldId].count(idom))
11150                     {
11151                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11152                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11153                     }
11154                 }
11155               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11156             }
11157         }
11158     }
11159
11160   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11161   grid->BuildLinks();
11162
11163   CHRONOSTOP(50);
11164   counters::stats();
11165   return true;
11166 }
11167
11168 /*!
11169  * \brief Double nodes on some external faces and create flat elements.
11170  * Flat elements are mainly used by some types of mechanic calculations.
11171  *
11172  * Each group of the list must be constituted of faces.
11173  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11174  * @param theElems - list of groups of faces, where a group of faces is a set of
11175  * SMDS_MeshElements sorted by Id.
11176  * @return TRUE if operation has been completed successfully, FALSE otherwise
11177  */
11178 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11179 {
11180   MESSAGE("-------------------------------------------------");
11181   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11182   MESSAGE("-------------------------------------------------");
11183
11184   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11185
11186   // --- For each group of faces
11187   //     duplicate the nodes, create a flat element based on the face
11188   //     replace the nodes of the faces by their clones
11189
11190   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11191   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11192   clonedNodes.clear();
11193   intermediateNodes.clear();
11194   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11195   mapOfJunctionGroups.clear();
11196
11197   for (int idom = 0; idom < theElems.size(); idom++)
11198     {
11199       const TIDSortedElemSet& domain = theElems[idom];
11200       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11201       for (; elemItr != domain.end(); ++elemItr)
11202         {
11203           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11204           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11205           if (!aFace)
11206             continue;
11207           // MESSAGE("aFace=" << aFace->GetID());
11208           bool isQuad = aFace->IsQuadratic();
11209           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11210
11211           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11212
11213           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11214           while (nodeIt->more())
11215             {
11216               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11217               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11218               if (isMedium)
11219                 ln2.push_back(node);
11220               else
11221                 ln0.push_back(node);
11222
11223               const SMDS_MeshNode* clone = 0;
11224               if (!clonedNodes.count(node))
11225                 {
11226                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11227                   clonedNodes[node] = clone;
11228                 }
11229               else
11230                 clone = clonedNodes[node];
11231
11232               if (isMedium)
11233                 ln3.push_back(clone);
11234               else
11235                 ln1.push_back(clone);
11236
11237               const SMDS_MeshNode* inter = 0;
11238               if (isQuad && (!isMedium))
11239                 {
11240                   if (!intermediateNodes.count(node))
11241                     {
11242                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11243                       intermediateNodes[node] = inter;
11244                     }
11245                   else
11246                     inter = intermediateNodes[node];
11247                   ln4.push_back(inter);
11248                 }
11249             }
11250
11251           // --- extrude the face
11252
11253           vector<const SMDS_MeshNode*> ln;
11254           SMDS_MeshVolume* vol = 0;
11255           vtkIdType aType = aFace->GetVtkType();
11256           switch (aType)
11257           {
11258             case VTK_TRIANGLE:
11259               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11260               // MESSAGE("vol prism " << vol->GetID());
11261               ln.push_back(ln1[0]);
11262               ln.push_back(ln1[1]);
11263               ln.push_back(ln1[2]);
11264               break;
11265             case VTK_QUAD:
11266               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11267               // MESSAGE("vol hexa " << vol->GetID());
11268               ln.push_back(ln1[0]);
11269               ln.push_back(ln1[1]);
11270               ln.push_back(ln1[2]);
11271               ln.push_back(ln1[3]);
11272               break;
11273             case VTK_QUADRATIC_TRIANGLE:
11274               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11275                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11276               // MESSAGE("vol quad prism " << vol->GetID());
11277               ln.push_back(ln1[0]);
11278               ln.push_back(ln1[1]);
11279               ln.push_back(ln1[2]);
11280               ln.push_back(ln3[0]);
11281               ln.push_back(ln3[1]);
11282               ln.push_back(ln3[2]);
11283               break;
11284             case VTK_QUADRATIC_QUAD:
11285 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11286 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11287 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11288               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11289                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11290                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11291               // MESSAGE("vol quad hexa " << vol->GetID());
11292               ln.push_back(ln1[0]);
11293               ln.push_back(ln1[1]);
11294               ln.push_back(ln1[2]);
11295               ln.push_back(ln1[3]);
11296               ln.push_back(ln3[0]);
11297               ln.push_back(ln3[1]);
11298               ln.push_back(ln3[2]);
11299               ln.push_back(ln3[3]);
11300               break;
11301             case VTK_POLYGON:
11302               break;
11303             default:
11304               break;
11305           }
11306
11307           if (vol)
11308             {
11309               stringstream grpname;
11310               grpname << "jf_";
11311               grpname << idom;
11312               int idg;
11313               string namegrp = grpname.str();
11314               if (!mapOfJunctionGroups.count(namegrp))
11315                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11316               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11317               if (sgrp)
11318                 sgrp->Add(vol->GetID());
11319             }
11320
11321           // --- modify the face
11322
11323           aFace->ChangeNodes(&ln[0], ln.size());
11324         }
11325     }
11326   return true;
11327 }
11328
11329 /*!
11330  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11331  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11332  *  groups of faces to remove inside the object, (idem edges).
11333  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11334  */
11335 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11336                                       const TopoDS_Shape& theShape,
11337                                       SMESH_NodeSearcher* theNodeSearcher,
11338                                       const char* groupName,
11339                                       std::vector<double>&   nodesCoords,
11340                                       std::vector<std::vector<int> >& listOfListOfNodes)
11341 {
11342   MESSAGE("--------------------------------");
11343   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11344   MESSAGE("--------------------------------");
11345
11346   // --- zone of volumes to remove is given :
11347   //     1 either by a geom shape (one or more vertices) and a radius,
11348   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11349   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11350   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11351   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11352   //     defined by it's name.
11353
11354   SMESHDS_GroupBase* groupDS = 0;
11355   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11356   while ( groupIt->more() )
11357     {
11358       groupDS = 0;
11359       SMESH_Group * group = groupIt->next();
11360       if ( !group ) continue;
11361       groupDS = group->GetGroupDS();
11362       if ( !groupDS || groupDS->IsEmpty() ) continue;
11363       std::string grpName = group->GetName();
11364       //MESSAGE("grpName=" << grpName);
11365       if (grpName == groupName)
11366         break;
11367       else
11368         groupDS = 0;
11369     }
11370
11371   bool isNodeGroup = false;
11372   bool isNodeCoords = false;
11373   if (groupDS)
11374     {
11375       if (groupDS->GetType() != SMDSAbs_Node)
11376         return;
11377       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11378     }
11379
11380   if (nodesCoords.size() > 0)
11381     isNodeCoords = true; // a list o nodes given by their coordinates
11382   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11383
11384   // --- define groups to build
11385
11386   int idg; // --- group of SMDS volumes
11387   string grpvName = groupName;
11388   grpvName += "_vol";
11389   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11390   if (!grp)
11391     {
11392       MESSAGE("group not created " << grpvName);
11393       return;
11394     }
11395   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11396
11397   int idgs; // --- group of SMDS faces on the skin
11398   string grpsName = groupName;
11399   grpsName += "_skin";
11400   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11401   if (!grps)
11402     {
11403       MESSAGE("group not created " << grpsName);
11404       return;
11405     }
11406   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11407
11408   int idgi; // --- group of SMDS faces internal (several shapes)
11409   string grpiName = groupName;
11410   grpiName += "_internalFaces";
11411   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11412   if (!grpi)
11413     {
11414       MESSAGE("group not created " << grpiName);
11415       return;
11416     }
11417   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11418
11419   int idgei; // --- group of SMDS faces internal (several shapes)
11420   string grpeiName = groupName;
11421   grpeiName += "_internalEdges";
11422   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11423   if (!grpei)
11424     {
11425       MESSAGE("group not created " << grpeiName);
11426       return;
11427     }
11428   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11429
11430   // --- build downward connectivity
11431
11432   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11433   meshDS->BuildDownWardConnectivity(true);
11434   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11435
11436   // --- set of volumes detected inside
11437
11438   std::set<int> setOfInsideVol;
11439   std::set<int> setOfVolToCheck;
11440
11441   std::vector<gp_Pnt> gpnts;
11442   gpnts.clear();
11443
11444   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11445     {
11446       MESSAGE("group of nodes provided");
11447       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11448       while ( elemIt->more() )
11449         {
11450           const SMDS_MeshElement* elem = elemIt->next();
11451           if (!elem)
11452             continue;
11453           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11454           if (!node)
11455             continue;
11456           SMDS_MeshElement* vol = 0;
11457           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11458           while (volItr->more())
11459             {
11460               vol = (SMDS_MeshElement*)volItr->next();
11461               setOfInsideVol.insert(vol->getVtkId());
11462               sgrp->Add(vol->GetID());
11463             }
11464         }
11465     }
11466   else if (isNodeCoords)
11467     {
11468       MESSAGE("list of nodes coordinates provided");
11469       int i = 0;
11470       int k = 0;
11471       while (i < nodesCoords.size()-2)
11472         {
11473           double x = nodesCoords[i++];
11474           double y = nodesCoords[i++];
11475           double z = nodesCoords[i++];
11476           gp_Pnt p = gp_Pnt(x, y ,z);
11477           gpnts.push_back(p);
11478           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11479           k++;
11480         }
11481     }
11482   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11483     {
11484       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11485       TopTools_IndexedMapOfShape vertexMap;
11486       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11487       gp_Pnt p = gp_Pnt(0,0,0);
11488       if (vertexMap.Extent() < 1)
11489         return;
11490
11491       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11492         {
11493           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11494           p = BRep_Tool::Pnt(vertex);
11495           gpnts.push_back(p);
11496           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11497         }
11498     }
11499
11500   if (gpnts.size() > 0)
11501     {
11502       int nodeId = 0;
11503       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11504       if (startNode)
11505         nodeId = startNode->GetID();
11506       MESSAGE("nodeId " << nodeId);
11507
11508       double radius2 = radius*radius;
11509       MESSAGE("radius2 " << radius2);
11510
11511       // --- volumes on start node
11512
11513       setOfVolToCheck.clear();
11514       SMDS_MeshElement* startVol = 0;
11515       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11516       while (volItr->more())
11517         {
11518           startVol = (SMDS_MeshElement*)volItr->next();
11519           setOfVolToCheck.insert(startVol->getVtkId());
11520         }
11521       if (setOfVolToCheck.empty())
11522         {
11523           MESSAGE("No volumes found");
11524           return;
11525         }
11526
11527       // --- starting with central volumes then their neighbors, check if they are inside
11528       //     or outside the domain, until no more new neighbor volume is inside.
11529       //     Fill the group of inside volumes
11530
11531       std::map<int, double> mapOfNodeDistance2;
11532       mapOfNodeDistance2.clear();
11533       std::set<int> setOfOutsideVol;
11534       while (!setOfVolToCheck.empty())
11535         {
11536           std::set<int>::iterator it = setOfVolToCheck.begin();
11537           int vtkId = *it;
11538           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11539           bool volInside = false;
11540           vtkIdType npts = 0;
11541           vtkIdType* pts = 0;
11542           grid->GetCellPoints(vtkId, npts, pts);
11543           for (int i=0; i<npts; i++)
11544             {
11545               double distance2 = 0;
11546               if (mapOfNodeDistance2.count(pts[i]))
11547                 {
11548                   distance2 = mapOfNodeDistance2[pts[i]];
11549                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11550                 }
11551               else
11552                 {
11553                   double *coords = grid->GetPoint(pts[i]);
11554                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11555                   distance2 = 1.E40;
11556                   for (int j=0; j<gpnts.size(); j++)
11557                     {
11558                       double d2 = aPoint.SquareDistance(gpnts[j]);
11559                       if (d2 < distance2)
11560                         {
11561                           distance2 = d2;
11562                           if (distance2 < radius2)
11563                             break;
11564                         }
11565                     }
11566                   mapOfNodeDistance2[pts[i]] = distance2;
11567                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11568                 }
11569               if (distance2 < radius2)
11570                 {
11571                   volInside = true; // one or more nodes inside the domain
11572                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11573                   break;
11574                 }
11575             }
11576           if (volInside)
11577             {
11578               setOfInsideVol.insert(vtkId);
11579               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11580               int neighborsVtkIds[NBMAXNEIGHBORS];
11581               int downIds[NBMAXNEIGHBORS];
11582               unsigned char downTypes[NBMAXNEIGHBORS];
11583               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11584               for (int n = 0; n < nbNeighbors; n++)
11585                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11586                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11587             }
11588           else
11589             {
11590               setOfOutsideVol.insert(vtkId);
11591               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11592             }
11593           setOfVolToCheck.erase(vtkId);
11594         }
11595     }
11596
11597   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11598   //     If yes, add the volume to the inside set
11599
11600   bool addedInside = true;
11601   std::set<int> setOfVolToReCheck;
11602   while (addedInside)
11603     {
11604       MESSAGE(" --------------------------- re check");
11605       addedInside = false;
11606       std::set<int>::iterator itv = setOfInsideVol.begin();
11607       for (; itv != setOfInsideVol.end(); ++itv)
11608         {
11609           int vtkId = *itv;
11610           int neighborsVtkIds[NBMAXNEIGHBORS];
11611           int downIds[NBMAXNEIGHBORS];
11612           unsigned char downTypes[NBMAXNEIGHBORS];
11613           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11614           for (int n = 0; n < nbNeighbors; n++)
11615             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11616               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11617         }
11618       setOfVolToCheck = setOfVolToReCheck;
11619       setOfVolToReCheck.clear();
11620       while  (!setOfVolToCheck.empty())
11621         {
11622           std::set<int>::iterator it = setOfVolToCheck.begin();
11623           int vtkId = *it;
11624           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11625             {
11626               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11627               int countInside = 0;
11628               int neighborsVtkIds[NBMAXNEIGHBORS];
11629               int downIds[NBMAXNEIGHBORS];
11630               unsigned char downTypes[NBMAXNEIGHBORS];
11631               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11632               for (int n = 0; n < nbNeighbors; n++)
11633                 if (setOfInsideVol.count(neighborsVtkIds[n]))
11634                   countInside++;
11635               MESSAGE("countInside " << countInside);
11636               if (countInside > 1)
11637                 {
11638                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11639                   setOfInsideVol.insert(vtkId);
11640                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11641                   addedInside = true;
11642                 }
11643               else
11644                 setOfVolToReCheck.insert(vtkId);
11645             }
11646           setOfVolToCheck.erase(vtkId);
11647         }
11648     }
11649
11650   // --- map of Downward faces at the boundary, inside the global volume
11651   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11652   //     fill group of SMDS faces inside the volume (when several volume shapes)
11653   //     fill group of SMDS faces on the skin of the global volume (if skin)
11654
11655   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11656   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
11657   std::set<int>::iterator it = setOfInsideVol.begin();
11658   for (; it != setOfInsideVol.end(); ++it)
11659     {
11660       int vtkId = *it;
11661       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11662       int neighborsVtkIds[NBMAXNEIGHBORS];
11663       int downIds[NBMAXNEIGHBORS];
11664       unsigned char downTypes[NBMAXNEIGHBORS];
11665       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11666       for (int n = 0; n < nbNeighbors; n++)
11667         {
11668           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11669           if (neighborDim == 3)
11670             {
11671               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11672                 {
11673                   DownIdType face(downIds[n], downTypes[n]);
11674                   boundaryFaces[face] = vtkId;
11675                 }
11676               // if the face between to volumes is in the mesh, get it (internal face between shapes)
11677               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11678               if (vtkFaceId >= 0)
11679                 {
11680                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11681                   // find also the smds edges on this face
11682                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11683                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11684                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11685                   for (int i = 0; i < nbEdges; i++)
11686                     {
11687                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11688                       if (vtkEdgeId >= 0)
11689                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11690                     }
11691                 }
11692             }
11693           else if (neighborDim == 2) // skin of the volume
11694             {
11695               DownIdType face(downIds[n], downTypes[n]);
11696               skinFaces[face] = vtkId;
11697               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11698               if (vtkFaceId >= 0)
11699                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11700             }
11701         }
11702     }
11703
11704   // --- identify the edges constituting the wire of each subshape on the skin
11705   //     define polylines with the nodes of edges, equivalent to wires
11706   //     project polylines on subshapes, and partition, to get geom faces
11707
11708   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11709   std::set<int> emptySet;
11710   emptySet.clear();
11711   std::set<int> shapeIds;
11712
11713   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11714   while (itelem->more())
11715     {
11716       const SMDS_MeshElement *elem = itelem->next();
11717       int shapeId = elem->getshapeId();
11718       int vtkId = elem->getVtkId();
11719       if (!shapeIdToVtkIdSet.count(shapeId))
11720         {
11721           shapeIdToVtkIdSet[shapeId] = emptySet;
11722           shapeIds.insert(shapeId);
11723         }
11724       shapeIdToVtkIdSet[shapeId].insert(vtkId);
11725     }
11726
11727   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11728   std::set<DownIdType, DownIdCompare> emptyEdges;
11729   emptyEdges.clear();
11730
11731   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
11732   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11733     {
11734       int shapeId = itShape->first;
11735       MESSAGE(" --- Shape ID --- "<< shapeId);
11736       shapeIdToEdges[shapeId] = emptyEdges;
11737
11738       std::vector<int> nodesEdges;
11739
11740       std::set<int>::iterator its = itShape->second.begin();
11741       for (; its != itShape->second.end(); ++its)
11742         {
11743           int vtkId = *its;
11744           MESSAGE("     " << vtkId);
11745           int neighborsVtkIds[NBMAXNEIGHBORS];
11746           int downIds[NBMAXNEIGHBORS];
11747           unsigned char downTypes[NBMAXNEIGHBORS];
11748           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11749           for (int n = 0; n < nbNeighbors; n++)
11750             {
11751               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11752                 continue;
11753               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11754               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11755               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11756                 {
11757                   DownIdType edge(downIds[n], downTypes[n]);
11758                   if (!shapeIdToEdges[shapeId].count(edge))
11759                     {
11760                       shapeIdToEdges[shapeId].insert(edge);
11761                       int vtkNodeId[3];
11762                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11763                       nodesEdges.push_back(vtkNodeId[0]);
11764                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11765                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11766                     }
11767                 }
11768             }
11769         }
11770
11771       std::list<int> order;
11772       order.clear();
11773       if (nodesEdges.size() > 0)
11774         {
11775           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
11776           nodesEdges[0] = -1;
11777           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
11778           nodesEdges[1] = -1; // do not reuse this edge
11779           bool found = true;
11780           while (found)
11781             {
11782               int nodeTofind = order.back(); // try first to push back
11783               int i = 0;
11784               for (i = 0; i<nodesEdges.size(); i++)
11785                 if (nodesEdges[i] == nodeTofind)
11786                   break;
11787               if (i == nodesEdges.size())
11788                 found = false; // no follower found on back
11789               else
11790                 {
11791                   if (i%2) // odd ==> use the previous one
11792                     if (nodesEdges[i-1] < 0)
11793                       found = false;
11794                     else
11795                       {
11796                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
11797                         nodesEdges[i-1] = -1;
11798                       }
11799                   else // even ==> use the next one
11800                     if (nodesEdges[i+1] < 0)
11801                       found = false;
11802                     else
11803                       {
11804                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
11805                         nodesEdges[i+1] = -1;
11806                       }
11807                 }
11808               if (found)
11809                 continue;
11810               // try to push front
11811               found = true;
11812               nodeTofind = order.front(); // try to push front
11813               for (i = 0; i<nodesEdges.size(); i++)
11814                 if (nodesEdges[i] == nodeTofind)
11815                   break;
11816               if (i == nodesEdges.size())
11817                 {
11818                   found = false; // no predecessor found on front
11819                   continue;
11820                 }
11821               if (i%2) // odd ==> use the previous one
11822                 if (nodesEdges[i-1] < 0)
11823                   found = false;
11824                 else
11825                   {
11826                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
11827                     nodesEdges[i-1] = -1;
11828                   }
11829               else // even ==> use the next one
11830                 if (nodesEdges[i+1] < 0)
11831                   found = false;
11832                 else
11833                   {
11834                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
11835                     nodesEdges[i+1] = -1;
11836                   }
11837             }
11838         }
11839
11840
11841       std::vector<int> nodes;
11842       nodes.push_back(shapeId);
11843       std::list<int>::iterator itl = order.begin();
11844       for (; itl != order.end(); itl++)
11845         {
11846           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11847           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
11848         }
11849       listOfListOfNodes.push_back(nodes);
11850     }
11851
11852   //     partition geom faces with blocFissure
11853   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11854   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11855
11856   return;
11857 }
11858
11859
11860 //================================================================================
11861 /*!
11862  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11863  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11864  * \return TRUE if operation has been completed successfully, FALSE otherwise
11865  */
11866 //================================================================================
11867
11868 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11869 {
11870   // iterates on volume elements and detect all free faces on them
11871   SMESHDS_Mesh* aMesh = GetMeshDS();
11872   if (!aMesh)
11873     return false;
11874   //bool res = false;
11875   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11876   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11877   while(vIt->more())
11878   {
11879     const SMDS_MeshVolume* volume = vIt->next();
11880     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11881     vTool.SetExternalNormal();
11882     //const bool isPoly = volume->IsPoly();
11883     const int iQuad = volume->IsQuadratic();
11884     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11885     {
11886       if (!vTool.IsFreeFace(iface))
11887         continue;
11888       nbFree++;
11889       vector<const SMDS_MeshNode *> nodes;
11890       int nbFaceNodes = vTool.NbFaceNodes(iface);
11891       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11892       int inode = 0;
11893       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11894         nodes.push_back(faceNodes[inode]);
11895       if (iQuad) { // add medium nodes
11896         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11897           nodes.push_back(faceNodes[inode]);
11898         if ( nbFaceNodes == 9 ) // bi-quadratic quad
11899           nodes.push_back(faceNodes[8]);
11900       }
11901       // add new face based on volume nodes
11902       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11903         nbExisted++;
11904         continue; // face already exsist
11905       }
11906       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11907       nbCreated++;
11908     }
11909   }
11910   return ( nbFree==(nbExisted+nbCreated) );
11911 }
11912
11913 namespace
11914 {
11915   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11916   {
11917     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11918       return n;
11919     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11920   }
11921 }
11922 //================================================================================
11923 /*!
11924  * \brief Creates missing boundary elements
11925  *  \param elements - elements whose boundary is to be checked
11926  *  \param dimension - defines type of boundary elements to create
11927  *  \param group - a group to store created boundary elements in
11928  *  \param targetMesh - a mesh to store created boundary elements in
11929  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11930  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
11931  *                                boundary elements will be copied into the targetMesh
11932  *  \param toAddExistingBondary - if true, not only new but also pre-existing
11933  *                                boundary elements will be added into the new group
11934  *  \param aroundElements - if true, elements will be created on boundary of given
11935  *                          elements else, on boundary of the whole mesh.
11936  * \return nb of added boundary elements
11937  */
11938 //================================================================================
11939
11940 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11941                                        Bnd_Dimension           dimension,
11942                                        SMESH_Group*            group/*=0*/,
11943                                        SMESH_Mesh*             targetMesh/*=0*/,
11944                                        bool                    toCopyElements/*=false*/,
11945                                        bool                    toCopyExistingBoundary/*=false*/,
11946                                        bool                    toAddExistingBondary/*= false*/,
11947                                        bool                    aroundElements/*= false*/)
11948 {
11949   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11950   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11951   // hope that all elements are of the same type, do not check them all
11952   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11953     throw SALOME_Exception(LOCALIZED("wrong element type"));
11954
11955   if ( !targetMesh )
11956     toCopyElements = toCopyExistingBoundary = false;
11957
11958   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11959   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11960   int nbAddedBnd = 0;
11961
11962   // editor adding present bnd elements and optionally holding elements to add to the group
11963   SMESH_MeshEditor* presentEditor;
11964   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11965   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11966
11967   SMESH_MesherHelper helper( *myMesh );
11968   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11969   SMDS_VolumeTool vTool;
11970   TIDSortedElemSet avoidSet;
11971   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11972   int inode;
11973
11974   typedef vector<const SMDS_MeshNode*> TConnectivity;
11975
11976   SMDS_ElemIteratorPtr eIt;
11977   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11978   else                  eIt = elemSetIterator( elements );
11979
11980   while (eIt->more())
11981   {
11982     const SMDS_MeshElement* elem = eIt->next();
11983     const int              iQuad = elem->IsQuadratic();
11984
11985     // ------------------------------------------------------------------------------------
11986     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11987     // ------------------------------------------------------------------------------------
11988     vector<const SMDS_MeshElement*> presentBndElems;
11989     vector<TConnectivity>           missingBndElems;
11990     TConnectivity nodes, elemNodes;
11991     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11992     {
11993       vTool.SetExternalNormal();
11994       const SMDS_MeshElement* otherVol = 0;
11995       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11996       {
11997         if ( !vTool.IsFreeFace(iface, &otherVol) &&
11998              ( !aroundElements || elements.count( otherVol )))
11999           continue;
12000         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12001         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12002         if ( missType == SMDSAbs_Edge ) // boundary edges
12003         {
12004           nodes.resize( 2+iQuad );
12005           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12006           {
12007             for ( int j = 0; j < nodes.size(); ++j )
12008               nodes[j] =nn[i+j];
12009             if ( const SMDS_MeshElement* edge =
12010                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12011               presentBndElems.push_back( edge );
12012             else
12013               missingBndElems.push_back( nodes );
12014           }
12015         }
12016         else // boundary face
12017         {
12018           nodes.clear();
12019           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12020             nodes.push_back( nn[inode] ); // add corner nodes
12021           if (iQuad)
12022             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12023               nodes.push_back( nn[inode] ); // add medium nodes
12024           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12025           if ( iCenter > 0 )
12026             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12027
12028           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12029                                                                SMDSAbs_Face, /*noMedium=*/false ))
12030             presentBndElems.push_back( f );
12031           else
12032             missingBndElems.push_back( nodes );
12033
12034           if ( targetMesh != myMesh )
12035           {
12036             // add 1D elements on face boundary to be added to a new mesh
12037             const SMDS_MeshElement* edge;
12038             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12039             {
12040               if ( iQuad )
12041                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12042               else
12043                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12044               if ( edge && avoidSet.insert( edge ).second )
12045                 presentBndElems.push_back( edge );
12046             }
12047           }
12048         }
12049       }
12050     }
12051     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12052     {
12053       avoidSet.clear(), avoidSet.insert( elem );
12054       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12055                         SMDS_MeshElement::iterator() );
12056       elemNodes.push_back( elemNodes[0] );
12057       nodes.resize( 2 + iQuad );
12058       const int nbLinks = elem->NbCornerNodes();
12059       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12060       {
12061         nodes[0] = elemNodes[iN];
12062         nodes[1] = elemNodes[iN+1+iQuad];
12063         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12064           continue; // not free link
12065
12066         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12067         if ( const SMDS_MeshElement* edge =
12068              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12069           presentBndElems.push_back( edge );
12070         else
12071           missingBndElems.push_back( nodes );
12072       }
12073     }
12074
12075     // ---------------------------------
12076     // 2. Add missing boundary elements
12077     // ---------------------------------
12078     if ( targetMesh != myMesh )
12079       // instead of making a map of nodes in this mesh and targetMesh,
12080       // we create nodes with same IDs.
12081       for ( int i = 0; i < missingBndElems.size(); ++i )
12082       {
12083         TConnectivity& srcNodes = missingBndElems[i];
12084         TConnectivity  nodes( srcNodes.size() );
12085         for ( inode = 0; inode < nodes.size(); ++inode )
12086           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12087         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12088                                                                    missType,
12089                                                                    /*noMedium=*/false))
12090           continue;
12091         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12092         ++nbAddedBnd;
12093       }
12094     else
12095       for ( int i = 0; i < missingBndElems.size(); ++i )
12096       {
12097         TConnectivity& nodes = missingBndElems[i];
12098         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12099                                                                    missType,
12100                                                                    /*noMedium=*/false))
12101           continue;
12102         SMDS_MeshElement* elem =
12103           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12104         ++nbAddedBnd;
12105
12106         // try to set a new element to a shape
12107         if ( myMesh->HasShapeToMesh() )
12108         {
12109           bool ok = true;
12110           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12111           const int nbN = nodes.size() / (iQuad+1 );
12112           for ( inode = 0; inode < nbN && ok; ++inode )
12113           {
12114             pair<int, TopAbs_ShapeEnum> i_stype =
12115               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12116             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12117               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12118           }
12119           if ( ok && mediumShapes.size() > 1 )
12120           {
12121             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12122             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12123             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12124             {
12125               if (( ok = ( stype_i->first != stype_i_0.first )))
12126                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12127                                         aMesh->IndexToShape( stype_i_0.second ));
12128             }
12129           }
12130           if ( ok && mediumShapes.begin()->first == missShapeType )
12131             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12132         }
12133       }
12134
12135     // ----------------------------------
12136     // 3. Copy present boundary elements
12137     // ----------------------------------
12138     if ( toCopyExistingBoundary )
12139       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12140       {
12141         const SMDS_MeshElement* e = presentBndElems[i];
12142         TConnectivity nodes( e->NbNodes() );
12143         for ( inode = 0; inode < nodes.size(); ++inode )
12144           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12145         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12146       }
12147     else // store present elements to add them to a group
12148       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12149       {
12150         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12151       }
12152
12153   } // loop on given elements
12154
12155   // ---------------------------------------------
12156   // 4. Fill group with boundary elements
12157   // ---------------------------------------------
12158   if ( group )
12159   {
12160     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12161       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12162         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12163   }
12164   tgtEditor.myLastCreatedElems.Clear();
12165   tgtEditor2.myLastCreatedElems.Clear();
12166
12167   // -----------------------
12168   // 5. Copy given elements
12169   // -----------------------
12170   if ( toCopyElements && targetMesh != myMesh )
12171   {
12172     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12173     else                  eIt = elemSetIterator( elements );
12174     while (eIt->more())
12175     {
12176       const SMDS_MeshElement* elem = eIt->next();
12177       TConnectivity nodes( elem->NbNodes() );
12178       for ( inode = 0; inode < nodes.size(); ++inode )
12179         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12180       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12181
12182       tgtEditor.myLastCreatedElems.Clear();
12183     }
12184   }
12185   return nbAddedBnd;
12186 }