Salome HOME
23081: [CEA 1496] Control merge nodes behaviour: set fixed nodes
authoreap <eap@opencascade.com>
Fri, 10 Jul 2015 16:39:03 +0000 (19:39 +0300)
committereap <eap@opencascade.com>
Fri, 10 Jul 2015 16:39:03 +0000 (19:39 +0300)
16 files changed:
doc/salome/examples/transforming_meshes_ex05.py
doc/salome/gui/SMESH/images/mergenodes.png
doc/salome/gui/SMESH/images/mergenodes_auto.png
doc/salome/gui/SMESH/images/merging_nodes1.png [changed mode: 0755->0644]
doc/salome/gui/SMESH/images/merging_nodes2.png [changed mode: 0755->0644]
doc/salome/gui/SMESH/input/merging_nodes.doc
doc/salome/gui/SMESH/input/split_biquad_to_linear.doc
idl/SMESH_MeshEditor.idl
src/SMESH/SMESH_Mesh.cxx
src/SMESH/SMESH_MeshEditor.cxx
src/SMESHGUI/SMESHGUI_MergeDlg.cxx
src/SMESHGUI/SMESHGUI_MergeDlg.h
src/SMESHGUI/SMESH_msg_en.ts
src/SMESH_I/SMESH_MeshEditor_i.cxx
src/SMESH_I/SMESH_MeshEditor_i.hxx
src/SMESH_SWIG/smeshBuilder.py

index f99d50b..deba1c4 100644 (file)
@@ -1,10 +1,14 @@
 # Merging Nodes
 
-import SMESH_mechanic
+import SMESH_mechanic, SMESH
 mesh = SMESH_mechanic.mesh
 
 # merge nodes
-Tolerance = 25.0
+Tolerance = 4.0
+
+# prevent nodes located on geom edges from removal during merge:
+# create a group including all nodes on edges
+allSegs = mesh.MakeGroup( "all segments", SMESH.EDGE, SMESH.FT_ElemGeomType,'=', SMESH.Geom_EDGE )
 
 GroupsOfNodes =  mesh.FindCoincidentNodes(Tolerance)
-mesh.MergeNodes(GroupsOfNodes)  
+mesh.MergeNodes(GroupsOfNodes, NodesToKeep=allSegs)
index 499453d..ad11a12 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/mergenodes.png and b/doc/salome/gui/SMESH/images/mergenodes.png differ
index 625083f..99ff0d2 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/mergenodes_auto.png and b/doc/salome/gui/SMESH/images/mergenodes_auto.png differ
old mode 100755 (executable)
new mode 100644 (file)
index f64bdcb..7913b5e
Binary files a/doc/salome/gui/SMESH/images/merging_nodes1.png and b/doc/salome/gui/SMESH/images/merging_nodes1.png differ
old mode 100755 (executable)
new mode 100644 (file)
index 8d7cfdd..e54b6f0
Binary files a/doc/salome/gui/SMESH/images/merging_nodes2.png and b/doc/salome/gui/SMESH/images/merging_nodes2.png differ
index 8e2aa4f..439e1fe 100644 (file)
@@ -26,7 +26,23 @@ merging.</li>
   nodes. This check-box is enabled provided that the selected mesh
   includes quadratic elements.</li>
 <li><b>Exclude Groups</b> group box allows to ignore the nodes which
-belong to the specified mesh groups.
+belong to the specified mesh groups.</li>
+<li><b>Nodes to keep</b> group box allows to specify nodes to keep in
+  the mesh. (By default a node being the first in a group of
+  coincident nodes is kept.) It is possible to either select nodes in
+  the Viewer or select groups of any element type whose nodes will be
+  kept.
+  <ol>
+    <li>\a Selection button activates selection of nodes to keep.</li>
+    <li><b>Node IDs</b> button activates selection of nodes in the
+      Viewer.</li>
+    <li><b>Groups and Sub-meshes</b> button activates selection of
+      groups and sub-meshes.</li>
+    <li>\b Add button adds selected nodes or groups to the list.</li>
+    <li> Nodes or groups selected in the list can be removed using \b
+      Remove button.</li>
+  </ol>
+</li>
 </ul>
 
 <li><b>Automatic mode:</b>
@@ -77,9 +93,9 @@ nodes of selected groups in the 3D viewer.</li>
 <li>To confirm your choice click \b Apply or <b>Apply and Close</b> button.</li>
 </ol>
 
-\image html merging_nodes1.png "The initial object" 
+\image html merging_nodes1.png "The initial object"
 
-\image html merging_nodes2.png "The object has been merged with a very big tolerance"
+\image html merging_nodes2.png "The object has been merged"
 
 <br><b>See Also</b> a sample TUI Script of a 
 \ref tui_merging_nodes "Merge Nodes" operation.  
index 85002f9..ea4ac72 100644 (file)
@@ -10,7 +10,7 @@ So that
 - bi-quadratic quadrangle will be split into 4 linear quadrangles;
 - tri-quadratic hexahedron will be split into 8 linear hexahedra;
 - quadratic segments adjacent to the split bi-quadratic element will
-  be split into 2 liner segment.
+  be split into 2 liner segments.
 
 \image html split_biquad_to_linear_mesh.png "Mesh before and after splitting"
 
index 509e9ed..57f50fb 100644 (file)
@@ -658,7 +658,8 @@ module SMESH
                                        in  boolean             SeparateCornersAndMedium)
       raises (SALOME::SALOME_Exception);
 
-    void MergeNodes (in array_of_long_array GroupsOfNodes)
+    void MergeNodes (in array_of_long_array    GroupsOfNodes,
+                     in SMESH::ListOfIDSources NodesToKeep)
       raises (SALOME::SALOME_Exception);
 
     /*!
index a6e7edd..6905e75 100644 (file)
@@ -1334,7 +1334,7 @@ bool SMESH_Mesh::HasModificationsToDiscard() const
   // return true if the next Compute() will be partial and
   // existing but changed elements may prevent successful re-compute
   bool hasComputed = false, hasNotComputed = false;
-SMESH_subMeshIteratorPtr smIt( _subMeshHolder->GetIterator() );
+  SMESH_subMeshIteratorPtr smIt( _subMeshHolder->GetIterator() );
   while ( smIt->more() )
   {
     const SMESH_subMesh* aSubMesh = smIt->next();
index 54233ac..8ca7653 100644 (file)
@@ -7334,16 +7334,17 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
   // Fill nodeNodeMap and elems
 
   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
-  for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
+  for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
+  {
     list<const SMDS_MeshNode*>& nodes = *grIt;
     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
     const SMDS_MeshNode* nToKeep = *nIt;
-    //MESSAGE("node to keep " << nToKeep->GetID());
-    for ( ++nIt; nIt != nodes.end(); nIt++ ) {
+    for ( ++nIt; nIt != nodes.end(); nIt++ )
+    {
       const SMDS_MeshNode* nToRemove = *nIt;
-      nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
-      if ( nToRemove != nToKeep ) {
-        //MESSAGE("  node to remove " << nToRemove->GetID());
+      nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
+      if ( nToRemove != nToKeep )
+      {
         rmNodeIds.push_back( nToRemove->GetID() );
         AddToSameGroups( nToKeep, nToRemove, aMesh );
         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
@@ -7353,7 +7354,6 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
             sm->SetIsAlwaysComputed( true );
       }
-
       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
       while ( invElemIt->more() ) {
         const SMDS_MeshElement* elem = invElemIt->next();
index 5c62beb..d084d32 100644 (file)
@@ -95,7 +95,7 @@
 
 namespace
 {
-  enum ActionType { MERGE_NODES, MERGE_ELEMENTS };
+  enum ActionType { MERGE_NODES, MERGE_ELEMENTS, TYPE_AUTO=0, TYPE_MANUAL };
 }
 namespace SMESH
 {
@@ -363,7 +363,7 @@ SMESHGUI_MergeDlg::SMESHGUI_MergeDlg (SMESHGUI* theModule, int theAction)
   GroupMeshLayout->addWidget(LineEditMesh);
 
   /***************************************************************/
-  // Controls for switch dialog behaviour
+  // Controls for switch dialog behaviour (myTypeId)
 
   TypeBox = new QGroupBox( tr( "SMESH_MODE" ), this );
   GroupType = new QButtonGroup( this );
@@ -378,7 +378,7 @@ SMESHGUI_MergeDlg::SMESHGUI_MergeDlg (SMESHGUI* theModule, int theAction)
   aTypeBoxLayout->addWidget( rb1 );
   aTypeBoxLayout->addWidget( rb2 );
 
-  myTypeId = 0;
+  myTypeId = TYPE_AUTO;
 
   /***************************************************************/
   // Controls for coincident elements detecting
@@ -410,6 +410,32 @@ SMESHGUI_MergeDlg::SMESHGUI_MergeDlg (SMESHGUI* theModule, int theAction)
     GroupExcludeLayout->setMargin(MARGIN);
     GroupExcludeLayout->addWidget(ListExclude);
 
+    QGroupBox*  GroupKeep = new QGroupBox(tr("KEEP_NODES"), foo);
+    SelectKeepNodesButton = new QPushButton( GroupKeep );
+    SelectKeepNodesButton->setIcon( IconSelect );
+    QLabel*       selectLabel = new QLabel(tr("SELECT"));
+    QRadioButton*   idsButton = new QRadioButton(tr("NODE_IDS"), GroupKeep);
+    QRadioButton* groupButton = new QRadioButton(tr("GROUP_SUBMESH"), GroupKeep);
+    KeepFromButGroup = new QButtonGroup( this );
+    KeepFromButGroup->addButton( idsButton,   0 );
+    KeepFromButGroup->addButton( groupButton, 1 );
+    groupButton->setChecked( true );
+    KeepList = new QListWidget( GroupKeep );
+    KeepList->setSelectionMode(QAbstractItemView::ExtendedSelection);
+    KeepList->setFlow(QListView::TopToBottom);
+    AddKeepNodesButton    = new QPushButton(tr("SMESH_BUT_ADD"), GroupKeep );
+    RemoveKeepNodesButton = new QPushButton(tr("SMESH_BUT_REMOVE"), GroupKeep );
+    QGridLayout* GroupKeepLayout = new QGridLayout(GroupKeep);
+    GroupKeepLayout->setSpacing( SPACING );
+    GroupKeepLayout->setMargin ( MARGIN );
+    GroupKeepLayout->addWidget( SelectKeepNodesButton, 0, 0 );
+    GroupKeepLayout->addWidget( selectLabel,           0, 1 );
+    GroupKeepLayout->addWidget( idsButton,             0, 2 );
+    GroupKeepLayout->addWidget( groupButton,           0, 3, 1, 2 );
+    GroupKeepLayout->addWidget( KeepList,              1, 0, 2, 4 );
+    GroupKeepLayout->addWidget( AddKeepNodesButton,    1, 4, 1, 1 );
+    GroupKeepLayout->addWidget( RemoveKeepNodesButton, 2, 4, 1, 1 );
+
     QGridLayout* fooLayout = new QGridLayout( foo );
     fooLayout->setSpacing(SPACING);
     fooLayout->setMargin(0);
@@ -417,13 +443,27 @@ SMESHGUI_MergeDlg::SMESHGUI_MergeDlg (SMESHGUI* theModule, int theAction)
     fooLayout->addWidget(SpinBoxTolerance,         0, 1 );
     fooLayout->addWidget(SeparateCornersAndMedium, 1, 0 );
     fooLayout->addWidget(GroupExclude,             2, 0, 1, 2 );
+    fooLayout->addWidget(GroupKeep,                3, 0, 1, 2 );
     aCoincidentLayout->addWidget(foo);
+
+    // Costruction of the logical filter
+    QList<SUIT_SelectionFilter*> aListOfFilters;
+    aListOfFilters << new SMESH_TypeFilter (SMESH::SUBMESH)
+                   << new SMESH_TypeFilter (SMESH::GROUP);
+    mySubMeshOrGroupFilter =
+      new SMESH_LogicalFilter (aListOfFilters, SMESH_LogicalFilter::LO_OR, /*takeOwnership=*/true);
   }
   else {
-    TextLabelTolerance = 0;
-    SpinBoxTolerance = 0;
-    GroupExclude = 0;
-    ListExclude = 0;
+    TextLabelTolerance     = 0;
+    SpinBoxTolerance       = 0;
+    GroupExclude           = 0;
+    ListExclude            = 0;
+    KeepFromButGroup       = 0;
+    SelectKeepNodesButton  = 0;
+    AddKeepNodesButton     = 0;
+    RemoveKeepNodesButton  = 0;
+    KeepList               = 0;
+    mySubMeshOrGroupFilter = 0;
   }
 
   GroupCoincidentWidget = new QWidget(GroupCoincident);
@@ -546,7 +586,7 @@ void SMESHGUI_MergeDlg::Init()
 
   GroupType->button(0)->setChecked(true);
 
-  myEditCurrentArgument = (QWidget*)LineEditMesh; 
+  myEditCurrentArgument = (QWidget*)LineEditMesh;
 
   myActor = 0;
   mySubMeshOrGroup = SMESH::SMESH_subMesh::_nil();
@@ -555,13 +595,21 @@ void SMESHGUI_MergeDlg::Init()
 
   mySMESHGUI->SetActiveDialogBox((QDialog*)this);
   myIsBusy = false;
-  
+
   /* signals and slots connections */
   connect(buttonOk,     SIGNAL(clicked()), this, SLOT(ClickOnOk()));
   connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
   connect(buttonApply,  SIGNAL(clicked()), this, SLOT(ClickOnApply()));
   connect(buttonHelp,   SIGNAL(clicked()), this, SLOT(ClickOnHelp()));
 
+  if ( KeepList )
+  {
+    connect(SelectKeepNodesButton, SIGNAL (clicked()), this, SLOT(SetEditCurrentArgument()));
+    connect(KeepFromButGroup, SIGNAL (buttonClicked(int)), SLOT(onKeepNodeSourceChanged(int)));
+    connect(AddKeepNodesButton, SIGNAL (clicked()), this, SLOT(onAddKeepNode()));
+    connect(RemoveKeepNodesButton, SIGNAL (clicked()), this, SLOT(onRemoveKeepNode()));
+    connect(KeepList, SIGNAL (itemSelectionChanged()), this, SLOT(onSelectKeepNode()));
+  }
   connect(SelectMeshButton, SIGNAL (clicked()), this, SLOT(SetEditCurrentArgument()));
   connect(DetectButton, SIGNAL (clicked()), this, SLOT(onDetect()));
   connect(ListCoincident, SIGNAL (itemSelectionChanged()), this, SLOT(onSelectGroup()));
@@ -572,7 +620,7 @@ void SMESHGUI_MergeDlg::Init()
   connect(ListEdit, SIGNAL (itemSelectionChanged()), this, SLOT(onSelectElementFromGroup()));
   connect(AddElemButton, SIGNAL (clicked()), this, SLOT(onAddElement()));
   connect(RemoveElemButton, SIGNAL (clicked()), this, SLOT(onRemoveElement()));
-  connect(SetFirstButton, SIGNAL( clicked() ), this, SLOT( onSetFirst() ) );
+  connect(SetFirstButton, SIGNAL( clicked() ), this, SLOT( onSetFirst()));
   connect(GroupType, SIGNAL(buttonClicked(int)), this, SLOT(onTypeChanged(int)));
 
   connect(mySMESHGUI, SIGNAL (SignalDeactivateActiveDialog()), this, SLOT(DeactivateActiveDialog()));
@@ -638,7 +686,7 @@ bool SMESHGUI_MergeDlg::ClickOnApply()
     return false;
 
   try {
-    if (myTypeId == 0)
+    if (myTypeId == TYPE_AUTO)
       onDetect();
 
     SUIT_OverrideCursor aWaitCursor;
@@ -672,12 +720,51 @@ bool SMESHGUI_MergeDlg::ClickOnApply()
       aGroupsOfElements[anArrayNum++] = anIds.inout();
     }
 
+    SMESH::ListOfIDSources_var nodesToKeep;
+    SMESH::IDSource_wrap tmpIdSource;
+    if ( myAction == MERGE_NODES )
+    {
+      nodesToKeep = new SMESH::ListOfIDSources();
+      int i, nb = KeepList->count();
+      if ( isKeepNodesIDsSelection() )
+      {
+        SMESH::long_array_var anIdList = new SMESH::long_array();
+        anIdList->length(nb);
+        for (i = 0; i < nb; i++)
+          anIdList[i] = KeepList->item(i)->text().toInt();
+
+        if ( nb > 0 )
+        {
+          tmpIdSource = aMeshEditor->MakeIDSource( anIdList, SMESH::NODE );
+          nodesToKeep->length( 1 );
+          nodesToKeep[0] = SMESH::SMESH_IDSource::_duplicate( tmpIdSource.in() );
+        }
+      }
+      else
+      {
+        nodesToKeep->length( nb );
+        int nbObj = 0;
+        for (i = 0; i < nb; i++)
+        {
+          QString entry = KeepList->item( i )->data( Qt::UserRole ).toString();
+          Handle(SALOME_InteractiveObject) anIO =
+            new SALOME_InteractiveObject( entry.toStdString().c_str(), "SMESH" );
+          SMESH::SMESH_IDSource_var idSrc =
+            SMESH::IObjectToInterface<SMESH::SMESH_IDSource>( anIO );
+          if ( !idSrc->_is_nil() )
+            nodesToKeep[ nbObj++ ] = SMESH::SMESH_IDSource::_duplicate( idSrc );
+        }
+        nodesToKeep->length( nbObj );
+      }
+      KeepList->clear();
+    }
+
     if( myAction == MERGE_NODES )
-      aMeshEditor->MergeNodes (aGroupsOfElements.inout());
+      aMeshEditor->MergeNodes (aGroupsOfElements.inout(), nodesToKeep);
     else
       aMeshEditor->MergeElements (aGroupsOfElements.inout());
 
-    if ( myTypeId == 0 ) {
+    if ( myTypeId == TYPE_AUTO ) {
       if (myAction == MERGE_NODES )
         SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INFORMATION"),
                                      tr("SMESH_MERGED_NODES").arg(QString::number(ListCoincident->count()).toLatin1().data()));
@@ -685,18 +772,19 @@ bool SMESHGUI_MergeDlg::ClickOnApply()
         SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INFORMATION"),
                                      tr("SMESH_MERGED_ELEMENTS").arg(QString::number(ListCoincident->count()).toLatin1().data()));
     }
-      
+    nodesToKeep->length(0); // release before tmpIdSource calls UnRegister()
 
-  } catch(...) {
   }
-  
+  catch(...) {
+  }
+
   ListCoincident->clear();
-  
+
   myEditCurrentArgument = (QWidget*)LineEditMesh;
 
   SMESH::UpdateView();
   SMESHGUI::Modified();
-  
+
   return true;
 }
 
@@ -810,7 +898,7 @@ void SMESHGUI_MergeDlg::updateControls()
 {
   if (ListEdit->count() == 0)
     SetFirstButton->setEnabled(false);
-  bool enable = !(myMesh->_is_nil()) && (ListCoincident->count() || (myTypeId == 0));
+  bool enable = !(myMesh->_is_nil()) && (ListCoincident->count() || (myTypeId == TYPE_AUTO));
   buttonOk->setEnabled(enable);
   buttonApply->setEnabled(enable);
   DetectButton->setEnabled( !myMesh->_is_nil() );
@@ -823,6 +911,13 @@ void SMESHGUI_MergeDlg::updateControls()
                           myMesh->NbVolumesOfOrder( SMESH::ORDER_QUADRATIC ) > 0 ));
 
     SeparateCornersAndMedium->setEnabled( has2ndOrder );
+
+    if ( myEditCurrentArgument != KeepList )
+    {
+      AddKeepNodesButton->setEnabled( false );
+      RemoveKeepNodesButton->setEnabled( false );
+      KeepList->clearSelection();
+    }
   }
 }
 
@@ -1149,14 +1244,32 @@ void SMESHGUI_MergeDlg::SetEditCurrentArgument()
   mySelectionMgr->clearSelected();
   mySelectionMgr->clearFilters();
 
-  if (send == SelectMeshButton) {
+  if (send == SelectMeshButton)
+  {
     myEditCurrentArgument = (QWidget*)LineEditMesh;
     SMESH::SetPointRepresentation(false);
     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
       aViewWindow->SetSelectionMode(ActorSelection);
-    if (myTypeId == 1)
+    if (myTypeId == TYPE_MANUAL)
       mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter);
   }
+  else if ( send == SelectKeepNodesButton && send )
+  {
+    myEditCurrentArgument = (QWidget*)KeepList;
+    KeepList->setWrapping( isKeepNodesIDsSelection() );
+    if ( isKeepNodesIDsSelection() )
+    {
+      SMESH::SetPointRepresentation( true );
+      if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
+        aViewWindow->SetSelectionMode( NodeSelection );
+    }
+    else
+    {
+      if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
+        aViewWindow->SetSelectionMode( ActorSelection );
+      mySelectionMgr->installFilter( mySubMeshOrGroupFilter );
+    }
+  }
 
   myEditCurrentArgument->setFocus();
   connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument()));
@@ -1165,11 +1278,12 @@ void SMESHGUI_MergeDlg::SetEditCurrentArgument()
 
 //=================================================================================
 // function : SelectionIntoArgument()
-// purpose  : Called when selection as changed or other case
+// purpose  : Called when selection has changed or other case
 //=================================================================================
 void SMESHGUI_MergeDlg::SelectionIntoArgument()
 {
-  if (myEditCurrentArgument == (QWidget*)LineEditMesh) {
+  if (myEditCurrentArgument == (QWidget*)LineEditMesh)
+  {
     QString aString = "";
     LineEditMesh->setText(aString);
 
@@ -1196,6 +1310,9 @@ void SMESHGUI_MergeDlg::SelectionIntoArgument()
     myEntry = IO->getEntry();
     myMesh = SMESH::GetMeshByIO(IO);
 
+    if ( myEntry != aCurrentEntry && KeepList )
+      KeepList->clear();
+
     if (myMesh->_is_nil())
       return;
 
@@ -1205,7 +1322,7 @@ void SMESHGUI_MergeDlg::SelectionIntoArgument()
     if (!myActor)
       myActor = SMESH::FindActorByObject(myMesh);
 
-    if ( myActor && myTypeId == 1 && mySelector->IsSelectionEnabled() ) {
+    if ( myActor && myTypeId == TYPE_MANUAL && mySelector->IsSelectionEnabled() ) {
       mySubMeshOrGroup = SMESH::SMESH_IDSource::_nil();
       mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter);
 
@@ -1246,6 +1363,59 @@ void SMESHGUI_MergeDlg::SelectionIntoArgument()
 
     updateControls();
   }
+
+  else if (myEditCurrentArgument == (QWidget*)KeepList && KeepList)
+  {
+    AddKeepNodesButton->setEnabled( false );
+    RemoveKeepNodesButton->setEnabled( false );
+    if ( isKeepNodesIDsSelection() )
+    {
+      if (!myMesh->_is_nil() && !myActor)
+        myActor = SMESH::FindActorByObject(myMesh);
+
+      if ( mySelector && myActor )
+      {
+        KeepList->clearSelection();
+        QString anIDs = "";
+        int aNbNodes = SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), anIDs);
+        if (aNbNodes > 0)
+        {
+          QStringList anNodes = anIDs.split( " ", QString::SkipEmptyParts);
+          QList<QListWidgetItem*> listItemsToSel;
+          QListWidgetItem* anItem;
+          int nbFound = 0;
+          for (QStringList::iterator it = anNodes.begin(); it != anNodes.end(); ++it)
+          {
+            QList<QListWidgetItem*> found = KeepList->findItems(*it, Qt::MatchExactly);
+            foreach(anItem, found)
+              if (!anItem->isSelected())
+                listItemsToSel.push_back(anItem);
+            nbFound += found.count();
+          }
+          bool blocked = KeepList->signalsBlocked();
+          KeepList->blockSignals(true);
+          foreach(anItem, listItemsToSel) anItem->setSelected(true);
+          KeepList->blockSignals(blocked);
+          //onSelectKeepNode();
+          AddKeepNodesButton->setEnabled( nbFound < aNbNodes );
+          RemoveKeepNodesButton->setEnabled( nbFound > 0 );
+        }
+      }
+    }
+    else if ( !myMesh->_is_nil() )
+    {
+      SALOME_ListIO aList;
+      mySelectionMgr->selectedObjects(aList);
+      bool hasNewSelected = false;
+      SALOME_ListIteratorOfListIO anIt (aList);
+      for ( ; anIt.More() && !hasNewSelected; anIt.Next())
+        if ( anIt.Value()->hasEntry() )
+          hasNewSelected = isNewKeepNodesGroup( anIt.Value()->getEntry() );
+
+      AddKeepNodesButton->setEnabled( hasNewSelected );
+      //RemoveKeepNodesButton->setEnabled( KeepList->selectedItems().count() );
+    }
+  }
 }
 
 //=================================================================================
@@ -1332,7 +1502,8 @@ void SMESHGUI_MergeDlg::onTypeChanged (int id)
   myTypeId = id;
   switch (id)
   {
-  case 0: // automatic
+  case TYPE_AUTO: // automatic
+
     myIdPreview->SetPointsLabeled(false);
     SMESH::SetPointRepresentation(false);
     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
@@ -1345,7 +1516,8 @@ void SMESHGUI_MergeDlg::onTypeChanged (int id)
     GroupEdit->hide();
     break;
 
-  case 1: // manual
+  case TYPE_MANUAL: // manual
+
     SMESH::UpdateView();
 
     // Costruction of the logical filter
@@ -1383,3 +1555,168 @@ void SMESHGUI_MergeDlg::onTypeChanged (int id)
 
   SelectionIntoArgument();
 }
+
+//=======================================================================
+//function : isKeepNodesIDsSelection
+//purpose  : Return true of Nodes to keep are selected by IDs
+//=======================================================================
+
+bool SMESHGUI_MergeDlg::isKeepNodesIDsSelection()
+{
+  return KeepFromButGroup && KeepFromButGroup->checkedId() == 0;
+}
+
+//=======================================================================
+//function : isNewKeepNodesGroup
+//purpose  : Return true if an object with given entry is NOT present in KeepList
+//=======================================================================
+
+bool SMESHGUI_MergeDlg::isNewKeepNodesGroup( const char* entry )
+{
+  if ( !entry || isKeepNodesIDsSelection() )
+    return false;
+
+  for ( int i = 0; i < KeepList->count(); i++ )
+    if ( KeepList->item( i )->data( Qt::UserRole ).toString() == entry )
+      return false;
+
+  return true;
+}
+
+//=======================================================================
+//function : onAddKeepNode
+//purpose  : SLOT called when [Add] of Nodes To Keep group is pressed
+//=======================================================================
+
+void SMESHGUI_MergeDlg::onAddKeepNode()
+{
+  if ( myIsBusy )
+    return;
+  myIsBusy = true;
+
+  if ( isKeepNodesIDsSelection() )
+  {
+    //KeepList->clearSelection();
+    QString anIDs = "";
+    int aNbNodes = 0;
+    if ( myActor )
+      aNbNodes = SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), anIDs);
+    if (aNbNodes > 0)
+    {
+      QStringList anNodes = anIDs.split( " ", QString::SkipEmptyParts);
+      QList<QListWidgetItem*> listItemsToSel;
+      QListWidgetItem* anItem;
+      for (QStringList::iterator it = anNodes.begin(); it != anNodes.end(); ++it)
+      {
+        QList<QListWidgetItem*> found = KeepList->findItems(*it, Qt::MatchExactly);
+        if (found.count() == 0) {
+          anItem = new QListWidgetItem(*it);
+          KeepList->addItem(anItem);
+          if (!anItem->isSelected())
+            listItemsToSel.push_back(anItem);
+        }
+        else {
+          foreach(anItem, found)
+            if (!anItem->isSelected())
+              listItemsToSel.push_back(anItem);
+        }
+      }
+      bool blocked = KeepList->signalsBlocked();
+      KeepList->blockSignals(true);
+      foreach(anItem, listItemsToSel) anItem->setSelected(true);
+      KeepList->blockSignals(blocked);
+      //onSelectKeepNode();
+    }
+    RemoveKeepNodesButton->setEnabled( aNbNodes > 0 );
+  }
+  else
+  {
+    SALOME_ListIO aList;
+    mySelectionMgr->selectedObjects(aList);
+    SALOME_ListIteratorOfListIO anIt (aList);
+    for ( ; anIt.More(); anIt.Next()) {
+      Handle(SALOME_InteractiveObject) anIO = anIt.Value();
+      if ( isNewKeepNodesGroup( anIO->getEntry() ))
+      {
+        QListWidgetItem* anItem = new QListWidgetItem( anIO->getName() );
+        anItem->setData( Qt::UserRole, QString( anIO->getEntry() ));
+        KeepList->addItem(anItem);
+      }
+    }
+    //RemoveKeepNodesButton->setEnabled( KeepList->selectedItems().count() );
+  }
+
+  AddKeepNodesButton->setEnabled( false );
+
+  myIsBusy = false;
+}
+
+//=======================================================================
+//function : onRemoveKeepNode
+//purpose  : SLOT called when [Remove] of Nodes To Keep group is pressed
+//=======================================================================
+
+void SMESHGUI_MergeDlg::onRemoveKeepNode()
+{
+  // if ( isKeepNodesIDsSelection() )
+  // {
+  // }
+  // else
+  {
+    QList<QListWidgetItem*> selItems = KeepList->selectedItems();
+    QListWidgetItem* item;
+    foreach(item, selItems) delete item;
+  }
+  if ( isKeepNodesIDsSelection() )
+  {
+    AddKeepNodesButton->setEnabled( false );
+  }
+  RemoveKeepNodesButton->setEnabled( false );
+}
+
+//=======================================================================
+//function : onSelectKeepNode
+//purpose  : SLOT called when selection in KeepList changes
+//=======================================================================
+
+void SMESHGUI_MergeDlg::onSelectKeepNode()
+{
+  if ( myIsBusy || !isEnabled() ) return;
+  myIsBusy = true;
+
+  if ( isKeepNodesIDsSelection() )
+  {
+    if ( myActor )
+    {
+      mySelectionMgr->clearSelected();
+      TColStd_MapOfInteger aIndexes;
+      QList<QListWidgetItem*> selItems = KeepList->selectedItems();
+      QListWidgetItem* anItem;
+      foreach(anItem, selItems) aIndexes.Add(anItem->text().toInt());
+      mySelector->AddOrRemoveIndex(myActor->getIO(), aIndexes, false);
+      SALOME_ListIO aList;
+      aList.Append(myActor->getIO());
+      mySelectionMgr->setSelectedObjects(aList,false);
+
+      AddKeepNodesButton->setEnabled( false );
+      RemoveKeepNodesButton->setEnabled( aIndexes.Extent() > 0 );
+    }
+  }
+  else
+  {
+    RemoveKeepNodesButton->setEnabled( KeepList->selectedItems().count() );
+  }
+  myIsBusy = false;
+}
+
+//=======================================================================
+//function : onKeepNodeSourceChanged
+//purpose  : SLOT called when type of source of Nodes To Keep change from
+//           IDs to groups or vice versa
+//=======================================================================
+
+void SMESHGUI_MergeDlg::onKeepNodeSourceChanged(int isGroup)
+{
+  KeepList->clear();
+  SelectKeepNodesButton->click();
+}
index 7e777ea..754dde0 100644 (file)
@@ -81,6 +81,8 @@ private:
   void                      enterEvent( QEvent* );              /* mouse enter the QWidget */
   void                      keyPressEvent( QKeyEvent* );
   void                      onEditGroup();
+  bool                      isKeepNodesIDsSelection();
+  bool                      isNewKeepNodesGroup( const char* entry );
 
   void                      FindGravityCenter( TColStd_MapOfInteger&, 
                                                std::list<gp_XYZ>& );
@@ -99,12 +101,13 @@ private:
   SMESH::SMESH_IDSource_var mySubMeshOrGroup;
   SMESH_Actor*              myActor;
   SUIT_SelectionFilter*     myMeshOrSubMeshOrGroupFilter;
+  SUIT_SelectionFilter*     mySubMeshOrGroupFilter;
 
   SMESH::TIdPreview*        myIdPreview;
 
   int                       myAction;
   bool                      myIsBusy;
-  int                       myTypeId;
+  int                       myTypeId; // manual(1) or automatic(0)
 
   // Widgets
   QGroupBox*                GroupConstructors;
@@ -142,6 +145,12 @@ private:
   QGroupBox*                GroupExclude;
   QListWidget*              ListExclude;
 
+  QButtonGroup*             KeepFromButGroup;
+  QPushButton*              SelectKeepNodesButton;
+  QPushButton*              AddKeepNodesButton;
+  QPushButton*              RemoveKeepNodesButton;
+  QListWidget*              KeepList;
+
   QGroupBox*                TypeBox;
   QButtonGroup*             GroupType;
     
@@ -159,6 +168,10 @@ protected slots:
   void                      ClickOnHelp();
   void                      updateControls();
   void                      onDetect();
+  void                      onAddKeepNode();
+  void                      onRemoveKeepNode();
+  void                      onSelectKeepNode();
+  void                      onKeepNodeSourceChanged(int);
   void                      onAddGroup();
   void                      onRemoveGroup();
   void                      onSelectGroup();
index 3bdd72e..8eb6265 100644 (file)
@@ -5143,6 +5143,22 @@ Please select a group and try again</translation>
         <source>SEPARATE_CORNERS_AND_MEDIUM</source>
         <translation>No merge of corner and medium nodes</translation>
     </message>
+    <message>
+        <source>KEEP_NODES</source>
+        <translation>Nodes to keep</translation>
+    </message>
+    <message>
+        <source>GROUP_SUBMESH</source>
+        <translation>Groups and Sub-meshes</translation>
+    </message>
+    <message>
+        <source>SELECT</source>
+        <translation>Select: </translation>
+    </message>
+    <message>
+        <source></source>
+        <translation></translation>
+    </message>
 </context>
 <context>
     <name>SMESHGUI_ExtrusionAlongPathDlg</name>
index c0e4963..1c6f602 100644 (file)
@@ -4113,7 +4113,8 @@ FindCoincidentNodesOnPartBut(SMESH::SMESH_IDSource_ptr      theObject,
 //purpose  :
 //=======================================================================
 
-void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfNodes)
+void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfNodes,
+                                     const SMESH::ListOfIDSources&     NodesToKeep)
   throw (SALOME::SALOME_Exception)
 {
   SMESH_TRY;
@@ -4123,6 +4124,16 @@ void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfN
 
   TPythonDump aTPythonDump;
   aTPythonDump << this << ".MergeNodes([";
+
+  TIDSortedNodeSet setOfNodesToKeep;
+  for ( int i = 0; i < NodesToKeep.length(); ++i )
+  {
+    prepareIdSource( NodesToKeep[i] );
+    SMDS_ElemIteratorPtr nodeIt = myMesh_i->GetElements( NodesToKeep[i], SMESH::NODE );
+    while ( nodeIt->more() )
+      setOfNodesToKeep.insert( setOfNodesToKeep.end(), cast2Node( nodeIt->next() ));
+  }
+
   ::SMESH_MeshEditor::TListOfListOfNodes aListOfListOfNodes;
   for (int i = 0; i < GroupsOfNodes.length(); i++)
   {
@@ -4132,9 +4143,13 @@ void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfN
     for ( int j = 0; j < aNodeGroup.length(); j++ )
     {
       CORBA::Long index = aNodeGroup[ j ];
-      const SMDS_MeshNode * node = aMesh->FindNode(index);
-      if ( node )
-        aListOfNodes.push_back( node );
+      if ( const SMDS_MeshNode * node = aMesh->FindNode( index ))
+      {
+        if ( setOfNodesToKeep.count( node ))
+          aListOfNodes.push_front( node );
+        else
+          aListOfNodes.push_back( node );
+      }
     }
     if ( aListOfNodes.size() < 2 )
       aListOfListOfNodes.pop_back();
@@ -4142,9 +4157,10 @@ void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfN
     if ( i > 0 ) aTPythonDump << ", ";
     aTPythonDump << aNodeGroup;
   }
+
   getEditor().MergeNodes( aListOfListOfNodes );
 
-  aTPythonDump <<  "])";
+  aTPythonDump << "], " << NodesToKeep << ")";
 
   declareMeshModified( /*isReComputeSafe=*/false );
 
index 560afae..97fe0ba 100644 (file)
@@ -488,7 +488,8 @@ public:
                                     const SMESH::ListOfIDSources&  ExceptSubMeshOrGroups,
                                     CORBA::Boolean                 SeparateCornersAndMedium)
     throw (SALOME::SALOME_Exception);
-  void MergeNodes (const SMESH::array_of_long_array& GroupsOfNodes)
+  void MergeNodes (const SMESH::array_of_long_array& GroupsOfNodes,
+                   const SMESH::ListOfIDSources&     NodesToKeep )
     throw (SALOME::SALOME_Exception);
   void FindEqualElements(SMESH::SMESH_IDSource_ptr      Object,
                          SMESH::array_of_long_array_out GroupsOfElementsID)
index 3e3b675..5610d35 100644 (file)
@@ -1609,6 +1609,8 @@ class Mesh:
     #  @return SMESH.Hypothesis_Status
     #  @ingroup l2_hypotheses
     def AddHypothesis(self, hyp, geom=0):
+        if isinstance( hyp, geomBuilder.GEOM._objref_GEOM_Object ):
+            hyp, geom = geom, hyp
         if isinstance( hyp, Mesh_Algorithm ):
             hyp = hyp.GetAlgorithm()
             pass
@@ -4428,9 +4430,18 @@ class Mesh:
     #  @param GroupsOfNodes a list of groups of nodes IDs for merging
     #         (e.g. [[1,12,13],[25,4]], then nodes 12, 13 and 4 will be removed and replaced
     #         by nodes 1 and 25 correspondingly in all elements and groups
+    #  @param NodesToKeep nodes to keep in the mesh: a list of groups, sub-meshes or node IDs.
+    #         If @a NodesToKeep does not include a node to keep for some group to merge,
+    #         then the first node in the group is kept.
     #  @ingroup l2_modif_trsf
-    def MergeNodes (self, GroupsOfNodes):
-        self.editor.MergeNodes(GroupsOfNodes)
+    def MergeNodes (self, GroupsOfNodes, NodesToKeep=[]):
+        unRegister = genObjUnRegister()
+        if NodesToKeep:
+            if isinstance( NodesToKeep, list ) and isinstance( NodesToKeep[0], int ):
+                NodesToKeep = self.GetIDSource( NodesToKeep, SMESH.NODE )
+            if not isinstance( NodesToKeep, list ):
+                NodesToKeep = [ NodesToKeep ]
+        self.editor.MergeNodes(GroupsOfNodes,NodesToKeep)
 
     ## Finds the elements built on the same nodes.
     #  @param MeshOrSubMeshOrGroup Mesh or SubMesh, or Group of elements for searching