Salome HOME
Merge branch V7_3_1_BR
[modules/smesh.git] / src / SMESHGUI / SMESHGUI.cxx
index 6fb58ce..336363e 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -6,7 +6,7 @@
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
 // License as published by the Free Software Foundation; either
-// version 2.1 of the License.
+// version 2.1 of the License, or (at your option) any later version.
 //
 // This library is distributed in the hope that it will be useful,
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -44,6 +44,7 @@
 #include "SMESHGUI_DuplicateNodesDlg.h"
 #include "SMESHGUI_ExtrusionAlongPathDlg.h"
 #include "SMESHGUI_ExtrusionDlg.h"
+#include "SMESHGUI_FieldSelectorWdg.h"
 #include "SMESHGUI_FileInfoDlg.h"
 #include "SMESHGUI_FileValidator.h"
 #include "SMESHGUI_FilterDlg.h"
 // of auto-color picking up
 #define SIMPLE_AUTOCOLOR
 
-//namespace{
+namespace
+{
   // Declarations
   //=============================================================
   void ImportMeshesFromFile(SMESH::SMESH_Gen_ptr theComponentMesh,
 
   void Control( int theCommandID );
 
-
   // Definitions
-  //=============================================================
+  //================================================================================
+  /*!
+   * \brief Reads meshes from file
+   */
+  //================================================================================
+
   void ImportMeshesFromFile( SMESH::SMESH_Gen_ptr theComponentMesh,
                              int theCommandID )
   {
     if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
       anInitialPath = QDir::currentPath();
 
+    QList< QPair< GEOM::ListOfFields_var, QString > > aFieldList;
+
     // Get a file name to write in and additional otions
     if ( isUNV || isDAT || isGMF ) // Export w/o options
     {
       QStringList checkBoxes;
       checkBoxes << QObject::tr("SMESH_AUTO_GROUPS") << QObject::tr("SMESH_AUTO_DIM");
 
+      SMESHGUI_FieldSelectorWdg* fieldSelWdg = new SMESHGUI_FieldSelectorWdg();
+      QList< QWidget* > wdgList;
+      if ( fieldSelWdg->GetAllFeilds( aMeshList, aFieldList ))
+        wdgList.append( fieldSelWdg );
+
       SalomeApp_CheckFileDlg* fd =
-        new SalomeApp_CheckFileDlg ( SMESHGUI::desktop(), false, checkBoxes, true, true );
+        new SalomeApp_CheckFileDlg ( SMESHGUI::desktop(), false, checkBoxes, true, true, wdgList );
       fd->setWindowTitle( aTitle );
       fd->setNameFilters( filters );
       fd->selectNameFilter( aDefaultFilter );
       }
       toCreateGroups = fd->IsChecked(0);
       toFindOutDim   = fd->IsChecked(1);
+      fieldSelWdg->GetSelectedFeilds();
+      if ( !fieldSelWdg->parent() )
+        delete fieldSelWdg;
       delete fd;
     }
     else
           {
             SMESH::SMESH_IDSource_var aMeshOrGroup = (*aMeshIter).first;
             SMESH::SMESH_Mesh_var        aMeshItem = aMeshOrGroup->GetMesh();
-            if ( aMeshOrGroup->_is_equivalent( aMeshItem ))
+            const GEOM::ListOfFields&       fields = aFieldList[ aMeshIndex ].first.in();
+            const QString&            geoAssFields = aFieldList[ aMeshIndex ].second;
+            const bool                   hasFields = ( fields.length() || !geoAssFields.isEmpty() );
+            if ( !hasFields && aMeshOrGroup->_is_equivalent( aMeshItem ))
               aMeshItem->ExportToMEDX( aFilename.toUtf8().data(), toCreateGroups,
                                        aFormat, toOverwrite && aMeshIndex == 0, toFindOutDim );
             else
               aMeshItem->ExportPartToMED( aMeshOrGroup, aFilename.toUtf8().data(), toCreateGroups,
-                                          aFormat, toOverwrite && aMeshIndex == 0, toFindOutDim );
+                                          aFormat, toOverwrite && aMeshIndex == 0, toFindOutDim,
+                                          fields, geoAssFields.toLatin1().data() );
           }
         }
         else if ( isSAUV )
         int deltaF = 0, deltaV = 0;
         int elem0dSize   = 1;
         int ballSize     = 1;
+        double ballScale = 1.0;
         int edgeWidth    = 1;
         int outlineWidth = 1;
         double shrinkCoef = 0.0;
             anActor->GetBallColor( color[0], color[1], color[2] );
             ballColor.setRgbF( color[0], color[1], color[2] );
             ballSize = qMax( (int)anActor->GetBallSize(), 1 ); // minimum allowed size is 1
+            ballScale = qMax( (double)anActor->GetBallScale(), 1e-2 ); // minimum allowed scale is 1e-2
             // outlines: color
             anActor->GetOutlineColor( color[0], color[1], color[2] );
             outlineColor.setRgbF( color[0], color[1], color[2] );
         // balls: color, size
         dlg.setBallColor( ballColor );
         dlg.setBallSize( ballSize );
+        dlg.setBallScale( ballScale );
         // orientation: color, scale, 3d flag
         dlg.setOrientationColor( orientationColor );
         dlg.setOrientationSize( int( orientationScale * 100. ) );
           elem0dSize       = dlg.elem0dSize();
           ballColor        = dlg.ballColor();
           ballSize         = dlg.ballSize();
+          ballScale        = dlg.ballScale();
           orientationColor = dlg.orientationColor();
           orientationScale = dlg.orientationSize() / 100.;
           orientation3d    = dlg.orientation3d();
             // balls: color, size
             anActor->SetBallColor( ballColor.redF(), ballColor.greenF(), ballColor.blueF() );
             anActor->SetBallSize( ballSize );
+            anActor->SetBallScale( ballScale );
             // orientation: color, scale, 3d flag
             anActor->SetFacesOrientationColor( orientationColor.redF(), orientationColor.greenF(), orientationColor.blueF() );
             anActor->SetFacesOrientationScale( orientationScale );
     QString RefType = CheckTypeObject(selected.First());
     SALOME_ListIteratorOfListIO It(selected);
     for ( ; It.More(); It.Next())
-      {
-        Handle(SALOME_InteractiveObject) IObject = It.Value();
-        QString Type = CheckTypeObject(IObject);
-        if (Type.compare(RefType) != 0)
-          return "Heterogeneous Selection";
-      }
+    {
+      Handle(SALOME_InteractiveObject) IObject = It.Value();
+      QString Type = CheckTypeObject(IObject);
+      if (Type.compare(RefType) != 0)
+        return "Heterogeneous Selection";
+    }
 
     return RefType;
   }
 
+} //namespace
 
-  void SMESHGUI::OnEditDelete()
-  {
-    // VSR 17/11/04: check if all objects selected belong to SMESH component --> start
-    LightApp_SelectionMgr* aSel = SMESHGUI::selectionMgr();
-    SALOME_ListIO selected; aSel->selectedObjects( selected, QString::null, false );
+void SMESHGUI::OnEditDelete()
+{
+  // VSR 17/11/04: check if all objects selected belong to SMESH component --> start
+  LightApp_SelectionMgr* aSel = SMESHGUI::selectionMgr();
+  SALOME_ListIO selected; aSel->selectedObjects( selected, QString::null, false );
 
-    _PTR(Study) aStudy = SMESH::GetActiveStudyDocument();
-    _PTR(StudyBuilder) aStudyBuilder = aStudy->NewBuilder();
-    _PTR(GenericAttribute) anAttr;
-    _PTR(AttributeIOR) anIOR;
-
-    int objectCount = 0;
-    QString aNameList;
-    QString aParentComponent = QString::null;
-    Handle(SALOME_InteractiveObject) anIO;
-    for( SALOME_ListIteratorOfListIO anIt( selected ); anIt.More(); anIt.Next() )
-    {
-      anIO = anIt.Value();
-      QString cur = anIO->getComponentDataType();
-      _PTR(SObject) aSO = aStudy->FindObjectID(anIO->getEntry());
-      if (aSO) {
-        // check if object is reference
-        _PTR(SObject) aRefSObj;
-        aNameList.append("\n    - ");
-        if ( aSO->ReferencedObject( aRefSObj ) ) {
-          QString aRefName = QString::fromStdString ( aRefSObj->GetName() );
-          aNameList.append( aRefName );
-          cur = QString::fromStdString ( aRefSObj->GetFatherComponent()->ComponentDataType() );
-        }
-        else
-          aNameList.append(anIO->getName());
-        objectCount++;
-      }
+  _PTR(Study) aStudy = SMESH::GetActiveStudyDocument();
+  _PTR(StudyBuilder) aStudyBuilder = aStudy->NewBuilder();
+  _PTR(GenericAttribute) anAttr;
+  _PTR(AttributeIOR) anIOR;
 
-      if( aParentComponent.isNull() )
-        aParentComponent = cur;
-      else if( !aParentComponent.isEmpty() && aParentComponent!=cur )
-        aParentComponent = "";
+  int objectCount = 0;
+  QString aNameList;
+  QString aParentComponent = QString::null;
+  Handle(SALOME_InteractiveObject) anIO;
+  for( SALOME_ListIteratorOfListIO anIt( selected ); anIt.More(); anIt.Next() )
+  {
+    anIO = anIt.Value();
+    QString cur = anIO->getComponentDataType();
+    _PTR(SObject) aSO = aStudy->FindObjectID(anIO->getEntry());
+    if (aSO) {
+      // check if object is reference
+      _PTR(SObject) aRefSObj;
+      aNameList.append("\n    - ");
+      if ( aSO->ReferencedObject( aRefSObj ) ) {
+        QString aRefName = QString::fromStdString ( aRefSObj->GetName() );
+        aNameList.append( aRefName );
+        cur = QString::fromStdString ( aRefSObj->GetFatherComponent()->ComponentDataType() );
+      }
+      else
+        aNameList.append(anIO->getName());
+      objectCount++;
     }
 
-    if ( objectCount == 0 )
-      return; // No Valid Objects Selected
+    if( aParentComponent.isNull() )
+      aParentComponent = cur;
+    else if( !aParentComponent.isEmpty() && aParentComponent!=cur )
+      aParentComponent = "";
+  }
 
-    if ( aParentComponent != SMESHGUI::GetSMESHGUI()->name() )  {
-      SUIT_MessageBox::warning( SMESHGUI::desktop(),
-                                QObject::tr("ERR_ERROR"),
-                                QObject::tr("NON_SMESH_OBJECTS_SELECTED").arg( SMESHGUI::GetSMESHGUI()->moduleName() ) );
-      return;
-    }
-    // VSR 17/11/04: check if all objects selected belong to SMESH component <-- finish
-    if (SUIT_MessageBox::warning
-        (SMESHGUI::desktop(),
-         QObject::tr("SMESH_WRN_WARNING"),
-         QObject::tr("SMESH_REALLY_DELETE").arg( objectCount ).arg( aNameList ),
-         SUIT_MessageBox::Yes | SUIT_MessageBox::No,
-         SUIT_MessageBox::Yes) != SUIT_MessageBox::Yes)
-      return;
+  if ( objectCount == 0 )
+    return; // No Valid Objects Selected
 
-    SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
+  if ( aParentComponent != SMESHGUI::GetSMESHGUI()->name() )  {
+    SUIT_MessageBox::warning( SMESHGUI::desktop(),
+                              QObject::tr("ERR_ERROR"),
+                              QObject::tr("NON_SMESH_OBJECTS_SELECTED").arg( SMESHGUI::GetSMESHGUI()->moduleName() ) );
+    return;
+  }
+  // VSR 17/11/04: check if all objects selected belong to SMESH component <-- finish
+  if (SUIT_MessageBox::warning
+      (SMESHGUI::desktop(),
+       QObject::tr("SMESH_WRN_WARNING"),
+       QObject::tr("SMESH_REALLY_DELETE").arg( objectCount ).arg( aNameList ),
+       SUIT_MessageBox::Yes | SUIT_MessageBox::No,
+       SUIT_MessageBox::Yes) != SUIT_MessageBox::Yes)
+    return;
 
-    // Put the whole hierarchy of sub-objects of the selected SO's into a list and
-    // then treat them all starting from the deepest objects (at list back)
-    std::list< _PTR(SObject) > listSO;
-    SALOME_ListIteratorOfListIO It(selected);
-    for( ; It.More(); It.Next()) // loop on selected IO's
-    {
-      Handle(SALOME_InteractiveObject) IObject = It.Value();
-      if(IObject->hasEntry()) {
-        _PTR(SObject) aSO = aStudy->FindObjectID(IObject->getEntry());
-
-        // disable removal of "SMESH" component object
-        if(aSO->FindAttribute(anAttr, "AttributeIOR")){
-          anIOR = anAttr;
-          if ( engineIOR() == anIOR->Value().c_str() )
-            continue;
-        }
-        //Check the referenced object
-        _PTR(SObject) aRefSObject;
-        if ( aSO && aSO->ReferencedObject( aRefSObject ) )
-          aSO = aRefSObject; // Delete main Object instead of reference
-
-        listSO.push_back( aSO );
-        std::list< _PTR(SObject) >::iterator itSO = --listSO.end();
-        for ( ; itSO != listSO.end(); ++itSO ) {
-          _PTR(ChildIterator) it = aStudy->NewChildIterator( *itSO );
-          for (it->InitEx(false); it->More(); it->Next())
-            listSO.push_back( it->Value() );
-        }
+  SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
+
+  // Put the whole hierarchy of sub-objects of the selected SO's into a list and
+  // then treat them all starting from the deepest objects (at list back)
+  std::list< _PTR(SObject) > listSO;
+  SALOME_ListIteratorOfListIO It(selected);
+  for( ; It.More(); It.Next()) // loop on selected IO's
+  {
+    Handle(SALOME_InteractiveObject) IObject = It.Value();
+    if(IObject->hasEntry()) {
+      _PTR(SObject) aSO = aStudy->FindObjectID(IObject->getEntry());
+
+      // disable removal of "SMESH" component object
+      if(aSO->FindAttribute(anAttr, "AttributeIOR")){
+        anIOR = anAttr;
+        if ( engineIOR() == anIOR->Value().c_str() )
+          continue;
+      }
+      //Check the referenced object
+      _PTR(SObject) aRefSObject;
+      if ( aSO && aSO->ReferencedObject( aRefSObject ) )
+        aSO = aRefSObject; // Delete main Object instead of reference
+
+      listSO.push_back( aSO );
+      std::list< _PTR(SObject) >::iterator itSO = --listSO.end();
+      for ( ; itSO != listSO.end(); ++itSO ) {
+        _PTR(ChildIterator) it = aStudy->NewChildIterator( *itSO );
+        for (it->InitEx(false); it->More(); it->Next())
+          listSO.push_back( it->Value() );
       }
     }
-    // Check if none of objects to delete is referred from outside
-    std::list< _PTR(SObject) >::reverse_iterator ritSO;
-    for ( ritSO = listSO.rbegin(); ritSO != listSO.rend(); ++ritSO )
-    {
-      _PTR(SObject) SO = *ritSO;
-      if ( !SO ) continue;
-      std::vector<_PTR(SObject)> aReferences = aStudy->FindDependances( *ritSO  );
-      for (size_t i = 0; i < aReferences.size(); i++) {
-        _PTR(SComponent) aComponent = aReferences[i]->GetFatherComponent();
-        std::string type = aComponent->ComponentDataType();
-        if ( type != "SMESH" )
-        {
-          SUIT_MessageBox::warning( anApp->desktop(),
-                                    QObject::tr("WRN_WARNING"),
-                                    QObject::tr("DEP_OBJECT") );
-          return; // outside SMESH, there is an object depending on a SMESH object 
-        }
+  }
+  // Check if none of objects to delete is referred from outside
+  std::list< _PTR(SObject) >::reverse_iterator ritSO;
+  for ( ritSO = listSO.rbegin(); ritSO != listSO.rend(); ++ritSO )
+  {
+    _PTR(SObject) SO = *ritSO;
+    if ( !SO ) continue;
+    std::vector<_PTR(SObject)> aReferences = aStudy->FindDependances( *ritSO  );
+    for (size_t i = 0; i < aReferences.size(); i++) {
+      _PTR(SComponent) aComponent = aReferences[i]->GetFatherComponent();
+      std::string type = aComponent->ComponentDataType();
+      if ( type != "SMESH" )
+      {
+        SUIT_MessageBox::warning( anApp->desktop(),
+                                  QObject::tr("WRN_WARNING"),
+                                  QObject::tr("DEP_OBJECT") );
+        return; // outside SMESH, there is an object depending on a SMESH object
       }
     }
+  }
 
-    // Call mesh->Clear() to prevent loading mesh from file caused by hypotheses removal
-    for( It.Initialize( selected ); It.More(); It.Next()) // loop on selected IO's
-    {
-      Handle(SALOME_InteractiveObject) IObject = It.Value();
-      SMESH::SMESH_Mesh_var mesh = SMESH::IObjectToInterface< SMESH::SMESH_Mesh >( IObject );
-      if ( !mesh->_is_nil() )
-        mesh->Clear();
-    }
+  // Call mesh->Clear() to prevent loading mesh from file caused by hypotheses removal
+  for( It.Initialize( selected ); It.More(); It.Next()) // loop on selected IO's
+  {
+    Handle(SALOME_InteractiveObject) IObject = It.Value();
+    SMESH::SMESH_Mesh_var mesh = SMESH::IObjectToInterface< SMESH::SMESH_Mesh >( IObject );
+    if ( !mesh->_is_nil() )
+      mesh->Clear();
+  }
 
-    // Treat SO's in the list starting from the back
-    aStudyBuilder->NewCommand();  // There is a transaction
-    for ( ritSO = listSO.rbegin(); ritSO != listSO.rend(); ++ritSO )
-    {
-      _PTR(SObject) SO = *ritSO;
-      if ( !SO ) continue;
-      std::string anEntry = SO->GetID();
+  // Treat SO's in the list starting from the back
+  aStudyBuilder->NewCommand();  // There is a transaction
+  for ( ritSO = listSO.rbegin(); ritSO != listSO.rend(); ++ritSO )
+  {
+    _PTR(SObject) SO = *ritSO;
+    if ( !SO ) continue;
+    std::string anEntry = SO->GetID();
 
-      /** Erase graphical object and remove all its data **/
-      if(SO->FindAttribute(anAttr, "AttributeIOR")) {
-        SMESH::RemoveVisualObjectWithActors( anEntry.c_str(), true);
-      }
-      /** Remove an object from data structures **/
-      SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( SMESH::SObjectToObject( SO ));
-      SMESH::SMESH_subMesh_var   aSubMesh = SMESH::SMESH_subMesh::_narrow( SMESH::SObjectToObject( SO ));
-      if ( !aGroup->_is_nil() ) {                          // DELETE GROUP
-        SMESH::SMESH_Mesh_var aMesh = aGroup->GetMesh();
-        aMesh->RemoveGroup( aGroup );
-      }
-      else if ( !aSubMesh->_is_nil() ) {                   // DELETE SUBMESH
-        SMESH::SMESH_Mesh_var aMesh = aSubMesh->GetFather();
-        aMesh->RemoveSubMesh( aSubMesh );
+    /** Erase graphical object and remove all its data **/
+    if(SO->FindAttribute(anAttr, "AttributeIOR")) {
+      SMESH::RemoveVisualObjectWithActors( anEntry.c_str(), true);
+    }
+    /** Remove an object from data structures **/
+    SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( SMESH::SObjectToObject( SO ));
+    SMESH::SMESH_subMesh_var   aSubMesh = SMESH::SMESH_subMesh::_narrow( SMESH::SObjectToObject( SO ));
+    if ( !aGroup->_is_nil() ) {                          // DELETE GROUP
+      SMESH::SMESH_Mesh_var aMesh = aGroup->GetMesh();
+      aMesh->RemoveGroup( aGroup );
+    }
+    else if ( !aSubMesh->_is_nil() ) {                   // DELETE SUBMESH
+      SMESH::SMESH_Mesh_var aMesh = aSubMesh->GetFather();
+      aMesh->RemoveSubMesh( aSubMesh );
 
-        _PTR(SObject) aMeshSO = SMESH::FindSObject(aMesh);
-        if (aMeshSO)
-          SMESH::ModifiedMesh(aMeshSO, false, aMesh->NbNodes()==0);
+      _PTR(SObject) aMeshSO = SMESH::FindSObject(aMesh);
+      if (aMeshSO)
+        SMESH::ModifiedMesh(aMeshSO, false, aMesh->NbNodes()==0);
+    }
+    else {
+      Handle(SALOME_InteractiveObject) IObject = new SALOME_InteractiveObject
+        ( anEntry.c_str(), engineIOR().toLatin1().data(), SO->GetName().c_str() );
+      QString objType = CheckTypeObject(IObject);
+      if ( objType == "Hypothesis" || objType == "Algorithm" ) {// DELETE HYPOTHESIS
+        SMESH::RemoveHypothesisOrAlgorithmOnMesh(IObject);
+        aStudyBuilder->RemoveObjectWithChildren( SO );
       }
-      else {
-        Handle(SALOME_InteractiveObject) IObject = new SALOME_InteractiveObject
-          ( anEntry.c_str(), engineIOR().toLatin1().data(), SO->GetName().c_str() );
-        QString objType = CheckTypeObject(IObject);
-        if ( objType == "Hypothesis" || objType == "Algorithm" ) {// DELETE HYPOTHESIS
-          SMESH::RemoveHypothesisOrAlgorithmOnMesh(IObject);
-          aStudyBuilder->RemoveObjectWithChildren( SO );
-        }
-        else {// default action: remove SObject from the study
-              // san - it's no use opening a transaction here until UNDO/REDO is provided in SMESH
-              //SUIT_Operation *op = new SALOMEGUI_ImportOperation(myActiveStudy);
-              //op->start();
-          aStudyBuilder->RemoveObjectWithChildren( SO );
-          //op->finish();
-        }
+      else {// default action: remove SObject from the study
+        // san - it's no use opening a transaction here until UNDO/REDO is provided in SMESH
+        //SUIT_Operation *op = new SALOMEGUI_ImportOperation(myActiveStudy);
+        //op->start();
+        aStudyBuilder->RemoveObjectWithChildren( SO );
+        //op->finish();
       }
-    } /* listSO back loop */
+    }
+  } /* listSO back loop */
 
-    aStudyBuilder->CommitCommand();
+  aStudyBuilder->CommitCommand();
 
-    /* Clear any previous selection */
-    SALOME_ListIO l1;
-    aSel->setSelectedObjects( l1 );
+  /* Clear any previous selection */
+  SALOME_ListIO l1;
+  aSel->setSelectedObjects( l1 );
 
-    SMESHGUI::GetSMESHGUI()->updateObjBrowser();
-  }
-//} namespace
+  SMESHGUI::GetSMESHGUI()->updateObjBrowser();
+}
 
 extern "C" {
   SMESHGUI_EXPORT CAM_Module* createModule()
@@ -2668,7 +2693,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
       else if ( theCommandID == 410 )
         aDlg = new SMESHGUI_UnionOfTrianglesDlg(this);
       else if ( theCommandID == 419 )
-        aDlg = new SMESHGUI_CuttingIntoTetraDlg(this);
+        aDlg = new SMESHGUI_SplitVolumesDlg(this);
       else
         aDlg = new SMESHGUI_CuttingOfQuadsDlg(this);
 
@@ -5016,6 +5041,8 @@ void SMESHGUI::createPreferences()
                              LightApp_Preferences::IntSpin, "SMESH", "elem0d_size");
   int ballSize = addPreference(tr("PREF_BALL_SIZE"), elemGroup,
                              LightApp_Preferences::IntSpin, "SMESH", "ball_elem_size");
+  double ballScale = addPreference(tr("PREF_BALL_SCALE"), elemGroup,
+                             LightApp_Preferences::DblSpin, "SMESH", "ball_elem_scale");
   int elemW  = addPreference(tr("PREF_WIDTH"), elemGroup,
                              LightApp_Preferences::IntSpin, "SMESH", "element_width");
   int outW  = addPreference(tr("PREF_OUTLINE_WIDTH"), elemGroup,
@@ -5029,6 +5056,10 @@ void SMESHGUI::createPreferences()
   setPreferenceProperty( ballSize, "min", 1 );
   setPreferenceProperty( ballSize, "max", 10 );
 
+  setPreferenceProperty( ballScale, "min", 1e-2 );
+  setPreferenceProperty( ballScale, "max", 1e7 );
+  setPreferenceProperty( ballScale, "step", 0.5 );
+
   setPreferenceProperty( elemW, "min", 1 );
   setPreferenceProperty( elemW, "max", 5 );
 
@@ -5701,6 +5732,7 @@ void SMESHGUI::storeVisualParameters (int savePoint)
                   sizeStr << QString::number((int)aSmeshActor->Get0DSize());
                   sizeStr << "ball";
                   sizeStr << QString::number((int)aSmeshActor->GetBallSize());
+                  sizeStr << QString::number((double)aSmeshActor->GetBallScale());
                   sizeStr << "shrink";
                   sizeStr << QString::number(aSmeshActor->GetShrinkFactor());
                   sizeStr << "orientation";
@@ -6285,6 +6317,7 @@ void SMESHGUI::restoreVisualParameters (int savePoint)
               int outlineWidth = -1;
               int elem0dSize = -1;
               int ballSize = -1;
+              double ballScale = -1.0;
               double shrinkSize = -1;
               double orientationSize = -1;
               bool orientation3d = false;
@@ -6312,11 +6345,16 @@ void SMESHGUI::restoreVisualParameters (int savePoint)
                   i++;
                 }
                 else if ( type == "ball" ) {
-                  // ball size is given as single integer value
-                  if ( i+1 >= sizes.count() ) break;                    // format error
-                  int v = sizes[i+1].toInt( &bOk ); if ( !bOk ) break;  // format error
-                  ballSize = v;
-                  i++;
+                  // balls are specified by two values: size:scale, where
+                  // - size - is a integer value specifying size
+                  // - scale - is a double value specifying scale factor
+                  if ( i+1 >= sizes.count() ) break;                       // format error
+                  int v1 = sizes[i+1].toInt( &bOk ); if ( !bOk ) break;    // format error
+                  if ( i+2 >= sizes.count() ) break;                       // format error
+                  double v2 = sizes[i+2].toDouble( &bOk ); if ( !bOk ) break; // format error
+                  ballSize = v1;
+                  ballScale = v2;
+                  i += 2;
                 }
                 else if ( type == "shrink" ) {
                   // shrink factor is given as single floating point value
@@ -6352,6 +6390,9 @@ void SMESHGUI::restoreVisualParameters (int savePoint)
               // ball size
               if ( ballSize > 0 )
                 aSmeshActor->SetBallSize( ballSize );
+              // ball scale
+              if ( ballScale > 0.0 )
+                aSmeshActor->SetBallScale( ballScale );
               // shrink factor
               if ( shrinkSize > 0 )
                 aSmeshActor->SetShrinkFactor( shrinkSize );