Salome HOME
Merge branch 'occ/shaper2smesh'
[modules/smesh.git] / src / SMESH_I / SMESH_2smeshpy.cxx
index 3c70da492c84906f8064b8953bb8ca0e1c33607c..689f498618521b4240972cf51e5ad9dd3c505a48 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2019  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
 #include <unistd.h>
 #endif
 
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyObject          ,Standard_Transient);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyCommand         ,Standard_Transient);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyHypothesisReader,Standard_Transient);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyGen             ,_pyObject);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyMesh            ,_pyObject);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pySubMesh         ,_pyObject);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyMeshEditor      ,_pyObject);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyHypothesis      ,_pyObject);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pySelfEraser      ,_pyObject);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyGroup           ,_pyObject);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyFilter          ,_pyObject);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyAlgorithm       ,_pyHypothesis);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyComplexParamHypo,_pyHypothesis);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyNumberOfSegmentsHyp,_pyHypothesis);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pyLayerDistributionHypo,_pyHypothesis);
-OCCT_IMPLEMENT_STANDARD_RTTIEXT(_pySegmentLengthAroundVertexHyp,_pyHypothesis);
+IMPLEMENT_STANDARD_RTTIEXT(_pyObject          ,Standard_Transient);
+IMPLEMENT_STANDARD_RTTIEXT(_pyCommand         ,Standard_Transient);
+IMPLEMENT_STANDARD_RTTIEXT(_pyHypothesisReader,Standard_Transient);
+IMPLEMENT_STANDARD_RTTIEXT(_pyGen             ,_pyObject);
+IMPLEMENT_STANDARD_RTTIEXT(_pyMesh            ,_pyObject);
+IMPLEMENT_STANDARD_RTTIEXT(_pySubMesh         ,_pyObject);
+IMPLEMENT_STANDARD_RTTIEXT(_pyMeshEditor      ,_pyObject);
+IMPLEMENT_STANDARD_RTTIEXT(_pyHypothesis      ,_pyObject);
+IMPLEMENT_STANDARD_RTTIEXT(_pySelfEraser      ,_pyObject);
+IMPLEMENT_STANDARD_RTTIEXT(_pyGroup           ,_pyObject);
+IMPLEMENT_STANDARD_RTTIEXT(_pyFilter          ,_pyObject);
+IMPLEMENT_STANDARD_RTTIEXT(_pyAlgorithm       ,_pyHypothesis);
+IMPLEMENT_STANDARD_RTTIEXT(_pyComplexParamHypo,_pyHypothesis);
+IMPLEMENT_STANDARD_RTTIEXT(_pyNumberOfSegmentsHyp,_pyHypothesis);
+IMPLEMENT_STANDARD_RTTIEXT(_pyLayerDistributionHypo,_pyHypothesis);
+IMPLEMENT_STANDARD_RTTIEXT(_pySegmentLengthAroundVertexHyp,_pyHypothesis);
 
 using namespace std;
 using SMESH::TPythonDump;
@@ -185,7 +185,7 @@ namespace {
     _AString comment;
 
     _pyID obj = cmd->GetObject();
-    if ( obj.Search( "print " ) == 1 )
+    if ( obj.Search( "print(" ) == 1 )
       return; // print statement
 
     if ( !obj.IsEmpty() && obj.Value( obj.Length() ) == ')' )
@@ -296,6 +296,8 @@ namespace {
     //   - FT_NodeConnectivityNumber= 22
     // v 8.5.0: FT_Undefined == 49, new items:
     //   - FT_Deflection2D          = 22
+    // v 9.3.0: FT_Undefined == 50, new items:
+    //   - FT_Length3D              = 22
     //
     // It's necessary to continue recording this history and to fill
     // undef2newItems (see below) accordingly.
@@ -319,6 +321,7 @@ namespace {
       undef2newItems[ 47 ].push_back( 22 );
       undef2newItems[ 48 ].push_back( 22 );
       undef2newItems[ 49 ].push_back( 22 );
+      undef2newItems[ 50 ].push_back( 22 );
 
       ASSERT( undef2newItems.rbegin()->first == SMESH::FT_Undefined );
     }
@@ -360,7 +363,7 @@ namespace {
   //================================================================================
   /*!
    * \brief Replaces "SMESH.PointStruct(x,y,z)" and "SMESH.DirStruct( SMESH.PointStruct(x,y,z))"
-   *        arguments of a given command by a list "[x,y,z]" if the list is accesible
+   *        arguments of a given command by a list "[x,y,z]" if the list is accessible
    *        type of argument.
    */
   //================================================================================
@@ -412,7 +415,7 @@ namespace {
   //================================================================================
   /*!
    * \brief Replaces "mesh.GetIDSource([id1,id2])" argument of a given command by
-   *        a list "[id1,id2]" if the list is an accesible type of argument.
+   *        a list "[id1,id2]" if the list is an accessible type of argument.
    */
   //================================================================================
 
@@ -443,6 +446,18 @@ namespace {
       }
     }
   }
+
+  bool _FilterArg( const _AString& theArg  )
+  {
+    static std::list<_AString> filteredArgs;
+    static bool initialized = false;
+    if ( !initialized ) {
+      initialized = true;
+      filteredArgs.push_back( "SMESH.MED_V2_1" );
+      filteredArgs.push_back( "SMESH.MED_V2_2" );
+    }  
+    return std::find( filteredArgs.begin(), filteredArgs.end(), theArg ) != filteredArgs.end();
+  }
 }
 
 //================================================================================
@@ -464,7 +479,6 @@ SMESH_2smeshpy::ConvertScript(std::list< TCollection_AsciiString >&     theScrip
                               Resource_DataMapOfAsciiStringAsciiString& theEntry2AccessorMethod,
                               Resource_DataMapOfAsciiStringAsciiString& theObjectNames,
                               std::set< TCollection_AsciiString >&      theRemovedObjIDs,
-                              SALOMEDS::Study_ptr&                      theStudy,
                               const bool                                theToKeepAllCommands)
 {
   std::list< TCollection_AsciiString >::iterator lineIt;
@@ -487,7 +501,6 @@ SMESH_2smeshpy::ConvertScript(std::list< TCollection_AsciiString >&     theScrip
   theGen = new _pyGen( theEntry2AccessorMethod,
                        theObjectNames,
                        theRemovedObjIDs,
-                       theStudy,
                        theToKeepAllCommands );
 
   for ( lineIt = theScriptLines.begin(); lineIt != theScriptLines.end(); ++lineIt )
@@ -501,7 +514,7 @@ SMESH_2smeshpy::ConvertScript(std::list< TCollection_AsciiString >&     theScrip
   MESSAGE_BEGIN ( std::endl << " ######## RESULT ######## " << std::endl<< std::endl );
 #endif
 
-  // clean commmands of removed objects depending on myIsPublished flag
+  // clean commands of removed objects depending on myIsPublished flag
   theGen->ClearCommands();
 
   // reorder commands after conversion
@@ -519,7 +532,6 @@ SMESH_2smeshpy::ConvertScript(std::list< TCollection_AsciiString >&     theScrip
   set<_pyID> createdObjects;
   createdObjects.insert( "smeshBuilder" );
   createdObjects.insert( "smesh" );
-  createdObjects.insert( "theStudy" );
   for ( cmd = theGen->GetCommands().begin(); cmd != theGen->GetCommands().end(); ++cmd )
   {
 #ifdef DUMP_CONVERSION
@@ -547,7 +559,6 @@ SMESH_2smeshpy::ConvertScript(std::list< TCollection_AsciiString >&     theScrip
 _pyGen::_pyGen(Resource_DataMapOfAsciiStringAsciiString& theEntry2AccessorMethod,
                Resource_DataMapOfAsciiStringAsciiString& theObjectNames,
                std::set< TCollection_AsciiString >&      theRemovedObjIDs,
-               SALOMEDS::Study_ptr&                      theStudy,
                const bool                                theToKeepAllCommands)
   : _pyObject( new _pyCommand( "", 0 )),
     myNbCommands( 0 ),
@@ -556,8 +567,8 @@ _pyGen::_pyGen(Resource_DataMapOfAsciiStringAsciiString& theEntry2AccessorMethod
     myRemovedObjIDs( theRemovedObjIDs ),
     myNbFilters( 0 ),
     myToKeepAllCommands( theToKeepAllCommands ),
-    myStudy( SALOMEDS::Study::_duplicate( theStudy )),
-    myGeomIDNb(0), myGeomIDIndex(-1)
+    myGeomIDNb(0), myGeomIDIndex(-1),
+    myShaperIDNb(0), myShaperIDIndex(-1)
 {
   // make that GetID() to return TPythonDump::SMESHGenName()
   GetCreationCmd()->Clear();
@@ -565,30 +576,38 @@ _pyGen::_pyGen(Resource_DataMapOfAsciiStringAsciiString& theEntry2AccessorMethod
   GetCreationCmd()->GetString() += "=";
 
   // Find 1st digit of study entry by which a GEOM object differs from a SMESH object
-  if ( !theObjectNames.IsEmpty() && !CORBA::is_nil( theStudy ))
+  if (!theObjectNames.IsEmpty())
   {
-    // find a GEOM entry
-    _pyID geomID;
-    SALOMEDS::SComponent_wrap geomComp = theStudy->FindComponent("GEOM");
-    if ( geomComp->_is_nil() ) return;
-    CORBA::String_var entry = geomComp->GetID();
-    geomID = entry.in();
-
-    // find a SMESH entry
-    _pyID smeshID;
-    Resource_DataMapIteratorOfDataMapOfAsciiStringAsciiString e2n( theObjectNames );
-    for ( ; e2n.More() && smeshID.IsEmpty(); e2n.Next() )
-      if ( _pyCommand::IsStudyEntry( e2n.Key() ))
-        smeshID = e2n.Key();
-
-    // find 1st difference between smeshID and geomID
-    if ( !geomID.IsEmpty() && !smeshID.IsEmpty() )
-      for ( int i = 1; i <= geomID.Length() && i <= smeshID.Length(); ++i )
-        if ( geomID.Value( i ) != smeshID.Value( i ))
-        {
-          myGeomIDNb = geomID.Value( i );
-          myGeomIDIndex = i;
-        }
+    // find a GEOM (aPass == 0) and SHAPERSTUDY (aPass == 1) entries
+    for(int aPass = 0; aPass < 2; aPass++) {
+      _pyID geomID;
+      SALOMEDS::SComponent_wrap geomComp = SMESH_Gen_i::getStudyServant()->
+        FindComponent(aPass == 0 ? "GEOM" : "SHAPERSTUDY");
+      if (geomComp->_is_nil()) continue;
+      CORBA::String_var entry = geomComp->GetID();
+      geomID = entry.in();
+
+      // find a SMESH entry
+      _pyID smeshID;
+      Resource_DataMapIteratorOfDataMapOfAsciiStringAsciiString e2n(theObjectNames);
+      for (; e2n.More() && smeshID.IsEmpty(); e2n.Next())
+        if (_pyCommand::IsStudyEntry(e2n.Key()))
+          smeshID = e2n.Key();
+
+      // find 1st difference between smeshID and geomID
+      if (!geomID.IsEmpty() && !smeshID.IsEmpty())
+        for (int i = 1; i <= geomID.Length() && i <= smeshID.Length(); ++i)
+          if (geomID.Value(i) != smeshID.Value(i))
+          {
+            if (aPass == 0) {
+              myGeomIDNb = geomID.Value(i);
+              myGeomIDIndex = i;
+            } else {
+              myShaperIDNb = geomID.Value(i);
+              myShaperIDIndex = i;
+            }
+          }
+    }
   }
 }
 
@@ -691,7 +710,7 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand
       _AString newCmd = indent + tab + ( aCommand->GetString().ToCString() + indent.Length() );
       _AString pasCmd = indent + tab + "pass"; // to keep valid if newCmd is erased
       _AString excStr = indent + "except:";
-      _AString msgStr = indent + "\tprint '"; msgStr += method + "() failed. Invalid file name?'";
+      _AString msgStr = indent + "\tprint('"; msgStr += method + "() failed. Invalid file name?')";
 
       myCommands.insert( --myCommands.end(), new _pyCommand( tryStr, myNbCommands ));
       aCommand->Clear();
@@ -743,15 +762,16 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand
     else if ( method == "MakeBoundaryElements")
       meshID = aCommand->GetResultValue(2);
 
-    if ( method.Search("MakeGroups") != -1  ||
-         method == "ExtrusionAlongPathX"    ||
-         method == "ExtrusionAlongPathObjX" ||
-         method == "DoubleNodeGroupNew"     ||
-         method == "DoubleNodeGroupsNew"    ||
-         method == "DoubleNodeElemGroupNew" ||
-         method == "DoubleNodeElemGroupsNew"||
-         method == "DoubleNodeElemGroup2New"||
-         method == "DoubleNodeElemGroups2New"
+    if ( method.Search("MakeGroups") != -1      ||
+         method == "ExtrusionAlongPathX"        ||
+         method == "ExtrusionAlongPathObjX"     ||
+         method == "DoubleNodeGroupNew"         ||
+         method == "DoubleNodeGroupsNew"        ||
+         method == "DoubleNodeElemGroupNew"     ||
+         method == "DoubleNodeElemGroupsNew"    ||
+         method == "DoubleNodeElemGroup2New"    ||
+         method == "DoubleNodeElemGroups2New"   ||
+         method == "AffectedElemGroupsInRegion"
          )
       groups = aCommand->GetResultValue();
     else if ( method == "MakeBoundaryMesh" )
@@ -998,10 +1018,10 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand )
     AddObject( mesh );
     return;
   }
-  if( method == "CreateMeshesFromMED" ||
-      method == "CreateMeshesFromSAUV"||
-      method == "CreateMeshesFromCGNS" ||
-      method == "CreateMeshesFromGMF" ) // command result is ( [mesh1,mesh2], status )
+  if ( method == "CreateMeshesFromMED" ||
+       method == "CreateMeshesFromSAUV"||
+       method == "CreateMeshesFromCGNS" ||
+       method == "CreateMeshesFromGMF" ) // command result is ( [mesh1,mesh2], status )
   {
     std::list< _pyID > meshIDs = theCommand->GetStudyEntries( theCommand->GetResultValue() );
     std::list< _pyID >::iterator meshID = meshIDs.begin();
@@ -1019,6 +1039,12 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand )
       theCommand->SetArg( 1, file );
     }
   }
+  if ( method == "CopyMeshWithGeom" )
+  {
+    std::list< _pyID > entries = theCommand->GetStudyEntries( theCommand->GetResultValue() );
+    Handle(_pyMesh) mesh = new _pyMesh( theCommand, entries.front() );
+    AddObject( mesh );
+  }
 
   // CreateHypothesis()
   if ( method == "CreateHypothesis" )
@@ -1081,9 +1107,21 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand )
   // Concatenate( [mesh1, ...], ... )
   else if ( method == "Concatenate" || method == "ConcatenateWithGroups")
   {
+    // OLD IDL: ( meshes, uniteGroups, toMerge, tol )
+    // IDL: ( meshes, uniteGroups, toMerge, tol, meshToAppendTo )
+    // PY:  ( meshes, uniteGroups, toMerge, tol, allGroups=False, name="", meshToAppendTo=None )
+    _pyID appendMesh = theCommand->GetArg( 5 );
     if ( method == "ConcatenateWithGroups" ) {
       theCommand->SetMethod( "Concatenate" );
-      theCommand->SetArg( theCommand->GetNbArgs() + 1, "True" );
+      theCommand->SetArg( 5, "True" );
+    }
+    else {
+      theCommand->SetArg( 5, "False" );
+    }
+    if ( !appendMesh.IsEmpty() && appendMesh != "None" )
+    {
+      appendMesh.Insert( 1, "meshToAppendTo=" );
+      theCommand->SetArg( theCommand->GetNbArgs() + 1, appendMesh );
     }
     Handle(_pyMesh) mesh = new _pyMesh( theCommand, theCommand->GetResultValue() );
     AddObject( mesh );
@@ -1104,7 +1142,7 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand )
   static TStringSet smeshpyMethods;
   if ( smeshpyMethods.empty() ) {
     const char * names[] =
-      { "SetEmbeddedMode","IsEmbeddedMode","SetCurrentStudy","GetCurrentStudy",
+      { "SetEmbeddedMode","IsEmbeddedMode","UpdateStudy","GetStudy",
         "GetPattern","GetSubShapesId",
         "" }; // <- mark of array end
     smeshpyMethods.Insert( names );
@@ -1215,7 +1253,7 @@ void _pyGen::PlaceSubmeshAfterItsCreation( Handle(_pyCommand) theCmdUsingSubmesh
 
 //================================================================================
 /*!
- * \brief Clean commmands of removed objects depending on myIsPublished flag
+ * \brief Clean commands of removed objects depending on myIsPublished flag
  */
 //================================================================================
 
@@ -1651,13 +1689,11 @@ Handle(_pyObject) _pyGen::FindObject( const _pyID& theObjID )  const
 
 bool _pyGen::IsGeomObject(const _pyID& theObjID) const
 {
-  if ( myGeomIDNb )
-  {
-    return ( myGeomIDIndex <= theObjID.Length() &&
-             int( theObjID.Value( myGeomIDIndex )) == myGeomIDNb &&
-             _pyCommand::IsStudyEntry( theObjID ));
-  }
-  return false;
+  bool isGeom = myGeomIDNb && myGeomIDIndex <= theObjID.Length() &&
+                int( theObjID.Value( myGeomIDIndex )) == myGeomIDNb;
+  bool isShaper = myShaperIDNb && myShaperIDIndex <= theObjID.Length() &&
+                  int( theObjID.Value( myShaperIDIndex )) == myShaperIDNb;
+  return ((isGeom || isShaper) && _pyCommand::IsStudyEntry( theObjID ));
 }
 
 //================================================================================
@@ -1676,7 +1712,7 @@ bool _pyGen::IsNotPublished(const _pyID& theObjID) const
   // either the SMESH object is not in study or it is a GEOM object
   if ( IsGeomObject( theObjID ))
   {
-    SALOMEDS::SObject_wrap so = myStudy->FindObjectID( theObjID.ToCString() );
+    SALOMEDS::SObject_wrap so = SMESH_Gen_i::getStudyServant()->FindObjectID( theObjID.ToCString() );
     if ( so->_is_nil() ) return true;
     CORBA::Object_var obj = so->GetObject();
     return CORBA::is_nil( obj );
@@ -1798,7 +1834,7 @@ _pyMesh::_pyMesh(const Handle(_pyCommand) theCreationCmd, const _pyID& meshId):
 
 void _pyMesh::Process( const Handle(_pyCommand)& theCommand )
 {
-  // some methods of SMESH_Mesh interface needs special conversion
+  // some methods of SMESH_Mesh interface need special conversion
   // to methods of Mesh python class
   //
   // 1. GetSubMesh(geom, name) + AddHypothesis(geom, algo)
@@ -1942,7 +1978,7 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand )
       if ( !obj->CanClear() )
         allGroupsRemoved = false;
     }
-    if ( nbGroupsBefore == myGroups.size() ) // no new _pyGroup created
+    if ( nbGroupsBefore == myGroups.size() && !obj.IsNull() ) // no new _pyGroup created
       obj->AddProcessedCmd( theCommand ); // to clear theCommand if all groups are removed
 
     if ( !allGroupsRemoved && !theGen->IsToKeepAllCommands() )
@@ -1951,7 +1987,7 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand )
       // if GetGroups() is just after Compute(), this can mean that the groups
       // were created by some algorithm and hence Compute() should not be discarded
       std::list< Handle(_pyCommand) >& cmdList = theGen->GetCommands();
-      std::list< Handle(_pyCommand) >::iterator cmd = cmdList.begin();
+      std::list< Handle(_pyCommand) >::reverse_iterator cmd = cmdList.rbegin();
       while ( (*cmd)->GetMethod() == "GetGroups" )
         ++cmd;
       if ( myLastComputeCmd == (*cmd))
@@ -1979,16 +2015,26 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand )
   // ----------------------------------------------------------------------
   else if ( theCommand->MethodStartsFrom( "Export" ))
   {
-    if ( method == "ExportToMED" ||  // ExportToMED()  --> ExportMED()
-         method == "ExportToMEDX" )  // ExportToMEDX() --> ExportMED()
+    if ( method == "ExportToMED"  || // ExportToMED()  --> ExportMED()
+         method == "ExportToMEDX" || // ExportToMEDX() --> ExportMED()
+         method == "ExportMED" )
     {
       theCommand->SetMethod( "ExportMED" );
-      if ( theCommand->GetNbArgs() == 5 )
+      // filter out deprecated version parameter
+      vector< _AString > args;
+      for ( int i = 1; i <= theCommand->GetNbArgs(); i++ ) {
+        if ( !_FilterArg( theCommand->GetArg( i ) ) )
+          args.push_back( theCommand->GetArg( i ) );
+      }
+      theCommand->RemoveArgs();
+      for ( unsigned int i = 0; i < args.size(); i++ )
+        theCommand->SetArg( i+1, args[i] );
+      if ( theCommand->GetNbArgs() == 4 )
       {
         // ExportToMEDX(...,autoDimension) -> ExportToMEDX(...,meshPart=None,autoDimension)
-        _AString autoDimension = theCommand->GetArg( 5 );
-        theCommand->SetArg( 5, "None" );
-        theCommand->SetArg( 6, autoDimension );
+        _AString autoDimension = theCommand->GetArg( 4 );
+        theCommand->SetArg( 4, "None" );
+        theCommand->SetArg( 5, autoDimension );
       }
     }
     else if ( method == "ExportCGNS" )
@@ -2014,12 +2060,32 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand )
       TCollection_AsciiString newMethod = method;
       newMethod.Remove( /*where=*/7, /*howmany=*/6 );
       theCommand->SetMethod( newMethod );
-      // make the 1st arg be the last one (or last but three for ExportMED())
-      _pyID partID = theCommand->GetArg( 1 );
-      int nbArgs = theCommand->GetNbArgs() - 3 * (newMethod == "ExportMED");
-      for ( int i = 2; i <= nbArgs; ++i )
-        theCommand->SetArg( i-1, theCommand->GetArg( i ));
-      theCommand->SetArg( nbArgs, partID );
+      // replace version parameter by minor
+      std::list< _AString > args;
+      for ( int i = 1; i <= theCommand->GetNbArgs(); i++ ) {
+        if ( _FilterArg( theCommand->GetArg( i )))
+          args.push_back( "minor=0");
+        else
+          args.push_back( theCommand->GetArg( i ));
+      }
+      // check the 1st arg meshPart, it must be SMESH_IDSource
+      _AString meshPart = args.front();
+      if ( _pyCommand::IsStudyEntry( meshPart ) ||
+           meshPart.Search( "Filter"      ) > 0 ||
+           meshPart.Search( "GetIDSource" ) > 0 ||
+           meshPart.Search( "meshPart"    ) > 0 )
+      {
+        // set the 1st arg meshPart
+        // - to 5th place for ExportMED command
+        // - to last place for the rest commands
+        std::list< _AString >::iterator newPos = args.end();
+        if ( newMethod == "ExportMED" )
+          std::advance( newPos = args.begin(), 5 );
+        args.splice( newPos, args, args.begin() );
+      }
+      std::list< _AString >::iterator a = args.begin();
+      for ( unsigned int i = 1; a != args.end(); ++i, ++a )
+        theCommand->SetArg( i, *a );
     }
     // remember file name
     theGen->AddExportedMesh( theCommand->GetArg( 1 ),
@@ -2088,7 +2154,7 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand )
   else if ( method == "GetMeshOrder" || method == "SetMeshOrder" )
   {
     // make commands GetSubMesh() returning sub-meshes be before using sub-meshes
-    // by GetMeshOrder() and SetMeshOrder(), since by defalut GetSubMesh()
+    // by GetMeshOrder() and SetMeshOrder(), since by default GetSubMesh()
     // commands are moved at the end of the script
     TCollection_AsciiString subIDs =
       ( method == "SetMeshOrder" ) ? theCommand->GetArg(1) : theCommand->GetResultValue();
@@ -2125,7 +2191,7 @@ bool _pyMesh::NeedMeshAccess( const Handle(_pyCommand)& theCommand )
     const char * names[] =
       { "ExportDAT","ExportUNV","ExportSTL","ExportSAUV", "RemoveGroup","RemoveGroupWithContents",
         "GetGroups","UnionGroups","IntersectGroups","CutGroups","CreateDimGroup","GetLog","GetId",
-        "ClearLog","GetStudyId","HasDuplicatedGroupNamesMED","GetMEDMesh","NbNodes","NbElements",
+        "ClearLog","HasDuplicatedGroupNamesMED","GetMEDMesh","NbNodes","NbElements",
         "NbEdges","NbEdgesOfOrder","NbFaces","NbFacesOfOrder","NbTriangles",
         "NbTrianglesOfOrder","NbQuadrangles","NbQuadranglesOfOrder","NbPolygons","NbVolumes",
         "NbVolumesOfOrder","NbTetras","NbTetrasOfOrder","NbHexas","NbHexasOfOrder",
@@ -2172,7 +2238,7 @@ void _pyMesh::Flush()
   list < Handle(_pyCommand) >::iterator cmd;
 
   // try to convert algo addition like this:
-  // mesh.AddHypothesis(geom, ALGO ) --> ALGO = mesh.Algo()
+  // mesh.AddHypothesis( geom, ALGO ) --> ALGO = mesh.Algo()
   for ( cmd = myAddHypCmds.begin(); cmd != myAddHypCmds.end(); ++cmd )
   {
     Handle(_pyCommand) addCmd = *cmd;
@@ -2434,12 +2500,12 @@ void _pyMeshEditor::Process( const Handle(_pyCommand)& theCommand)
       "MergeElements","MergeEqualElements","SewFreeBorders","SewConformFreeBorders",
       "FindCoincidentFreeBorders", "SewCoincidentFreeBorders",
       "SewBorderToSide","SewSideElements","ChangeElemNodes","GetLastCreatedNodes",
-      "GetLastCreatedElems",
+      "GetLastCreatedElems", "FaceGroupsSeparatedByEdges",
       "MirrorMakeMesh","MirrorObjectMakeMesh","TranslateMakeMesh","TranslateObjectMakeMesh",
       "Scale","ScaleMakeMesh","RotateMakeMesh","RotateObjectMakeMesh","MakeBoundaryMesh",
       "MakeBoundaryElements", "SplitVolumesIntoTetra","SplitHexahedraIntoPrisms",
       "DoubleElements","DoubleNodes","DoubleNode","DoubleNodeGroup","DoubleNodeGroups",
-      "DoubleNodeElem","DoubleNodeElemInRegion","DoubleNodeElemGroup",
+      "DoubleNodeElem","DoubleNodeElemInRegion","DoubleNodeElemGroup","AffectedElemGroupsInRegion",
       "DoubleNodeElemGroupInRegion","DoubleNodeElemGroups","DoubleNodeElemGroupsInRegion",
       "DoubleNodesOnGroupBoundaries","CreateFlatElementsOnFacesGroups","CreateHoleSkin"
       ,"" }; // <- mark of the end
@@ -3014,7 +3080,7 @@ void _pyHypothesis::rememberCmdOfParameter( const Handle(_pyCommand) & theComman
   // parameters are discriminated by method name
   _AString method = theCommand->GetMethod();
   if ( myAccumulativeMethods.count( method ))
-    return; // this method adds values and not override the previus value
+    return; // this method adds values and not override the previous value
 
   // discriminate commands setting different parameters via one method
   // by passing parameter names like e.g. SetOption("size", "0.2")
@@ -3099,8 +3165,7 @@ void _pyHypothesis::ComputeDiscarded( const Handle(_pyCommand)& theComputeCmd )
       continue;
     // check if a cmd is a sole command setting its parameter;
     // don't use method name for search as it can change
-    map<TCollection_AsciiString, list<Handle(_pyCommand)> >::iterator
-      m2cmds = myMeth2Commands.begin();
+    map<_AString, list<Handle(_pyCommand)> >::iterator m2cmds = myMeth2Commands.begin();
     for ( ; m2cmds != myMeth2Commands.end(); ++m2cmds )
     {
       list< Handle(_pyCommand)>& cmds = m2cmds->second;
@@ -3685,17 +3750,17 @@ const TCollection_AsciiString & _pyCommand::GetObject()
     if ( begPos < 1 ) {
       begPos = myString.Location( "=", 1, Length() ) + 1;
       // is '=' in the string argument (for example, name) or not
-      int nb1 = 0; // number of ' character at the left of =
-      int nb2 = 0; // number of " character at the left of =
-      for ( int i = 1; i < begPos-1; i++ ) {
-        if ( myString.Value( i )=='\'' )
-          nb1 += 1;
-        else if ( myString.Value( i )=='"' )
-          nb2 += 1;
-      }
-      // if number of ' or " is not divisible by 2,
+      int nb[4] = { 0, 0, 0, 0 }; // number of '"() character at the left of =
+      for ( int i = 1; i < begPos-1; i++ )
+        switch ( myString.Value( i )) {
+        case '\'': nb[0]++; break;
+        case '"' : nb[1]++; break;
+        case '(' : nb[2]++; break;
+        case ')' : nb[3]++; break;
+        }
+      // if = is inside a string or a list
       // then get an object at the start of the command
-      if ( nb1 % 2 != 0 || nb2 % 2 != 0 )
+      if ( nb[0] % 2 != 0 || nb[1] % 2 != 0 || nb[2] != nb[3])
         begPos = 1;
     }
     else {
@@ -3771,6 +3836,10 @@ bool _pyCommand::IsMethodCall()
 {
   if ( GetMethod().IsEmpty() )
     return false;
+  if ( myString.StartsWith("#") )
+    return false;
+  if ( myString.StartsWith("SHAPERSTUDY") ) // skip shaperstudy specific dump string analysis
+    return false;
   const char* s = myString.ToCString() + GetBegPos( METHOD_IND ) + myMeth.Length() - 1;
   return ( s[0] == '(' || s[1] == '(' );
 }
@@ -4001,7 +4070,7 @@ bool _pyCommand::IsID( const TCollection_AsciiString& str )
 
 //================================================================================
 /*!
- * \brief Finds entries in a sting
+ * \brief Finds entries in a string
  */
 //================================================================================
 
@@ -4086,7 +4155,7 @@ void _pyCommand::SetPart(int thePartIndex, const TCollection_AsciiString& thePar
 
 //================================================================================
 /*!
- * \brief Set agrument
+ * \brief Set argument
  *  \param index - The argument index, it counts from 1
  *  \param theArg - The argument string
  */
@@ -4999,40 +5068,33 @@ bool _pyStringFamily::Add( const char* str )
   const int minPrefixSize = 4;
 
   // count "smaller" strings with the same prefix
-  std::list< _AString >::iterator itLess = itStr; --itLess;
   int nbLess = 0;
-  for ( ; itLess != _strings.end(); --itLess )
+  std::list< _AString >::iterator itLess = itStr;
+  while ( itLess != _strings.begin() )
+  {
+    --itLess;
     if ( strncmp( str, itLess->ToCString(), minPrefixSize ) == 0 )
       ++nbLess;
     else
+    {
+      ++itLess;
       break;
-  ++itLess;
+    }
+  }
+  // itLess points to the 1st string with same prefix
+
   // count "greater" strings with the same prefix
-  std::list< _AString >::iterator itMore = itStr;
   int nbMore = 0;
+  std::list< _AString >::iterator itMore = itStr;
   for ( ; itMore != _strings.end(); ++itMore )
     if ( strncmp( str, itMore->ToCString(), minPrefixSize ) == 0 )
       ++nbMore;
     else
       break;
-  --itMore;
+  // itMore points to the 1st string with greater prefix
+
   if ( nbLess + nbMore > 1 ) // ------- ADD a NEW CHILD FAMILY -------------
   {
-    // look for a maximal prefix length
-    // int lessPrefSize = 3, morePrefSize = 3;
-    // if ( nbLess > 0 )
-    //   while( itLess->ToCString()[ lessPrefSize ] == str[ lessPrefSize ]  )
-    //     ++lessPrefSize;
-    // if ( nbMore > 0 )
-    //   while ( itMore->ToCString()[ morePrefSize ] == str[ morePrefSize ] )
-    //     ++morePrefSize;
-    // int prefixSize = 3;
-    // if ( nbLess == 0 )
-    //   prefixSize = morePrefSize;
-    // else if ( nbMore == 0 )
-    //   prefixSize = lessPrefSize;
-    // else
-    //   prefixSize = Min( lessPrefSize, morePrefSize );
     int prefixSize = minPrefixSize;
     _AString newPrefix ( str, prefixSize );
 
@@ -5053,9 +5115,9 @@ bool _pyStringFamily::Add( const char* str )
     for ( ; nbMore > 0; --nbMore, ++itStr )
       newSubFam._strings.push_back( itStr->ToCString() + prefixSize );
 
-    _strings.erase( itLess, ++itMore );
+    _strings.erase( itLess, itMore );
   }
-  else // to few string to make a family fot them
+  else // too few string to make a family for them
   {
     _strings.insert( itStr, str );
   }
@@ -5129,7 +5191,7 @@ int _pyStringFamily::isIn( const char* str )
       if (( len = itSub->isIn( str + itSub->_prefix.Length() )) >= 0 )
         return itSub->_prefix.Length() + len;
     }
-    else if ( cmp > 0 )
+    else if ( cmp < 0 )
       break;
   }
   if ( !_strings.empty() )