Merge C++ part of kleontev/38044_auto_repair

This commit is contained in:
NATHALIE GORE 2024-04-04 11:49:08 +02:00
parent e3ce834889
commit 14e1a694c4
14 changed files with 365 additions and 88 deletions

View File

@ -4292,6 +4292,16 @@ module GEOM
*/
GEOM_Object LimitTolerance (in GEOM_Object theObject, in double theTolerance);
/*!
* \brief Provides Python dump functionality for algorithms entirely implemented in Python.
* \param theObject Shape to be processed.
* \param result Shape of the algorithm execution.
* \param imports module import for current functon.
* \param funcName name of a Python function that we need to put into dump.
* \param args arguments of the Python function.
*/
void FuncToPythonDump(in GEOM_Object theObject, in GEOM_Object result, in string imports, in string funcName, in string args);
/*!
* \brief Return information on what has been done by the last called healing method.

View File

@ -152,6 +152,124 @@ static TCollection_AsciiString GetPublishCommands
void Prettify(TCollection_AsciiString& theScript);
// Helper functions
namespace
{
// Specifies a way to process a given function
enum FunctionProcessType { NOT_PROCESS, TO_PROCESS, UPDATE_DESCRIPTION };
// Starting string for an edge case where function was dumped from Python code
const Standard_CString funcFromPythonStartString = "from salome.geom.geomrepairadv";
//================================================================================
/*!
* \brief Checks if a description contents a function that was dumped from Python code
*/
//================================================================================
bool IsFunctionSetFromPython(const TCollection_AsciiString& aDescr)
{
// TODO: make it more generic and not depended on geomrepairadv name
return aDescr.Search(funcFromPythonStartString) != -1;
}
//================================================================================
/*!
* \brief Removes from description the part before specific import statement
*/
//================================================================================
void UpdateFuncFromPythonDescription(TCollection_AsciiString& aDescr)
{
const Standard_Integer startStringPos = aDescr.Search(funcFromPythonStartString);
MESSAGE("Description should start from pos: " << startStringPos);
if (startStringPos == -1)
{
MESSAGE("Can't find a string:\n" << funcFromPythonStartString << " \nin func description!");
return;
}
// Remove everything from the beginning till the starting point.
// Index starts from 1 not 0!
aDescr.Remove(1, startStringPos - 1);
MESSAGE("Updated func description: " << aDescr);
}
//================================================================================
/*!
* \brief Finds out how we should process a given function for Python dump
*/
//================================================================================
FunctionProcessType GetFunctionProcessingType(const Handle(GEOM_Function)& theFunction, const TDF_LabelMap& theProcessed, const TCollection_AsciiString& aDescr)
{
MESSAGE("Start check function dependencies...");
TDF_LabelSequence aSeq;
theFunction->GetDependency(aSeq);
const Standard_Integer aLen = aSeq.Length();
for (Standard_Integer i = 1; i <= aLen; i++) {
TDF_Label aRefLabel = aSeq.Value(i);
Handle(TDF_Reference) aRef;
if (!aRefLabel.FindAttribute(TDF_Reference::GetID(), aRef)) {
MESSAGE("Can't find TDF_Reference::GetID() attribute. Do not process.");
return NOT_PROCESS;
}
if (aRef.IsNull() || aRef->Get().IsNull()) {
MESSAGE("Reference to attribute is null. Do not process.");
return NOT_PROCESS;
}
Handle(TDataStd_TreeNode) aT;
if (!TDataStd_TreeNode::Find(aRef->Get(), aT)) {
MESSAGE("Can't find a tree node. Do not process.");
return NOT_PROCESS;
}
TDF_Label aDepLabel = aT->Label();
Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(aDepLabel);
if (aFunction.IsNull()) {
MESSAGE("Function is null. Do not process." << aFunction->GetDescription());
return NOT_PROCESS;
}
if (!theProcessed.Contains(aDepLabel)) {
// Special case for function dumped from Python, because it's appended to
// description of other function that should be rejected early.
// TODO: it's not clear if we need to check every given function or
// checking on this level is enough. At this moment it's better to stay here
// for performance reason.
if (IsFunctionSetFromPython(aDescr)) {
MESSAGE("Function set from Python. Do process with updated description.");
return UPDATE_DESCRIPTION;
}
MESSAGE("The dependency label is not in processed list. Do not process.");
return NOT_PROCESS;
}
}
MESSAGE("OK. Do process the function.");
return TO_PROCESS;
}
//================================================================================
/*!
* \brief Adds function's object to ignored for Python dump output
*/
//================================================================================
void AddFuncObjectToIgnored(const Handle(GEOM_Function)& theFunction, std::set<TCollection_AsciiString>& theIgnoreObjs)
{
TCollection_AsciiString anObjEntry;
TDF_Tool::Entry(theFunction->GetOwnerEntry(), anObjEntry);
theIgnoreObjs.insert(anObjEntry);
}
}
//================================================================================
/*!
* \brief Fix up the name of python variable
@ -1084,62 +1202,62 @@ bool ProcessFunction(Handle(GEOM_Function)& theFunction,
bool& theIsDumpCollected)
{
theIsDumpCollected = false;
if (theFunction.IsNull()) return false;
if (theProcessed.Contains(theFunction->GetEntry())) return false;
// pass functions, that depends on nonexisting ones
bool doNotProcess = false;
TDF_LabelSequence aSeq;
theFunction->GetDependency(aSeq);
Standard_Integer aLen = aSeq.Length();
for (Standard_Integer i = 1; i <= aLen && !doNotProcess; i++) {
TDF_Label aRefLabel = aSeq.Value(i);
Handle(TDF_Reference) aRef;
if (!aRefLabel.FindAttribute(TDF_Reference::GetID(), aRef)) {
doNotProcess = true;
}
else {
if (aRef.IsNull() || aRef->Get().IsNull()) {
doNotProcess = true;
}
else {
Handle(TDataStd_TreeNode) aT;
if (!TDataStd_TreeNode::Find(aRef->Get(), aT)) {
doNotProcess = true;
}
else {
TDF_Label aDepLabel = aT->Label();
Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(aDepLabel);
if (aFunction.IsNull()) doNotProcess = true;
else if (!theProcessed.Contains(aDepLabel)) doNotProcess = true;
}
}
}
}
if (doNotProcess) {
TCollection_AsciiString anObjEntry;
TDF_Tool::Entry(theFunction->GetOwnerEntry(), anObjEntry);
theIgnoreObjs.insert(anObjEntry);
if (theFunction.IsNull()) {
MESSAGE("Can't process a null function! Return.");
return false;
}
theProcessed.Add(theFunction->GetEntry());
TCollection_AsciiString aDescr = theFunction->GetDescription();
if(aDescr.Length() == 0) return false;
MESSAGE("The function description: " << aDescr);
//Check if its internal function which doesn't requires dumping
if(aDescr == "None") return false;
if (theProcessed.Contains(theFunction->GetEntry()))
return false;
// Check if a given function depends on nonexisting ones
const FunctionProcessType funcProcessType = GetFunctionProcessingType(theFunction, theProcessed, aDescr);
switch (funcProcessType)
{
case TO_PROCESS:
// Just process it
break;
case NOT_PROCESS:
{
// We don't need this function and its object in a dump
AddFuncObjectToIgnored(theFunction, theIgnoreObjs);
return false;
}
case UPDATE_DESCRIPTION:
// Edge case for a function that was dumped from Python.
// Get rid of the parent function description.
UpdateFuncFromPythonDescription(aDescr);
// A result object is already added by an algo script, then
// if we keep it in the dump it will be added twice on the script loading.
AddFuncObjectToIgnored(theFunction, theIgnoreObjs);
break;
default:
MESSAGE("Wrong type of the function processing!" << funcProcessType);
break;
}
theProcessed.Add(theFunction->GetEntry());
// Check the length only after its fucntion was added to the processed!
if(!aDescr.Length())
return false;
// Check if it's an internal function which doesn't require dumping
if(aDescr == "None")
return false;
//Check the very specific case of RestoreShape function,
//which is not dumped, but the result can be published by the user.
//We do not publish such objects to decrease danger of dumped script failure.
if(aDescr.Value(1) == '#') {
TCollection_AsciiString anObjEntry;
TDF_Tool::Entry(theFunction->GetOwnerEntry(), anObjEntry);
theIgnoreObjs.insert(anObjEntry);
AddFuncObjectToIgnored(theFunction, theIgnoreObjs);
return false;
}
@ -1156,6 +1274,9 @@ bool ProcessFunction(Handle(GEOM_Function)& theFunction,
//Replace parameter by notebook variables
ReplaceVariables(aDescr,theVariables);
// Check description, because we could lose entire command during variable processing
if (aDescr.IsEmpty())
return false;
//Process sketcher functions, replacing string command by calls to Sketcher interface
if ( ( aDescr.Search( "MakeSketcherOnPlane" ) != -1 ) || ( aDescr.Search( "MakeSketcher" ) != -1 ) ) {
@ -1295,8 +1416,7 @@ Handle(TColStd_HSequenceOfInteger) FindEntries(TCollection_AsciiString& theStrin
void ReplaceVariables(TCollection_AsciiString& theCommand,
const TVariablesList& theVariables)
{
if (SALOME::VerbosityActivated())
std::cout<<"Command : "<<theCommand<<std::endl;
MESSAGE("Command : " << theCommand);
if (SALOME::VerbosityActivated()) {
std::cout<<"All Entries:"<<std::endl;
@ -1312,8 +1432,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
if( aCommand.Length() == 0 )
break;
if (SALOME::VerbosityActivated())
std::cout<<"Sub-command : "<<aCommand<<std::endl;
MESSAGE("Sub-command : " << aCommand);
Standard_Integer aStartCommandPos = theCommand.Location(aCommand,1,theCommand.Length());
Standard_Integer aEndCommandPos = aStartCommandPos + aCommand.Length();
@ -1330,8 +1449,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
//Remove white spaces
anEntry.RightAdjust();
anEntry.LeftAdjust();
if(SALOME::VerbosityActivated())
std::cout<<"Result entry : '" <<anEntry<<"'"<<std::endl;
MESSAGE("Result entry : '" << anEntry << "'");
if ( anEntry.IsEmpty() ) {
aCommandIndex++;
@ -1348,8 +1466,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
anEntry.Remove( 1, 1 );
anEntry.RightAdjust();
anEntry.LeftAdjust();
if(SALOME::VerbosityActivated())
std::cout<<"Sub-entry : '" <<anEntry<<"'"<<std::endl;
MESSAGE("Sub-entry : '" << anEntry << "'");
}
//Find variables used for object construction
@ -1359,9 +1476,13 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
aStates = (*it).second;
if(!aStates) {
if(SALOME::VerbosityActivated())
std::cout<<"Valiables list empty!!!"<<std::endl;
MESSAGE("Can't find an entry among study objects!");
// We can't skip this because the entry can be used with automatically assigned name
// like "geomObj_1" to create other objects. Some tests will fail without this.
// MESSAGE("Can't find an entry among study objects! Skip this command.");
// theCommand.Remove(aStartCommandPos, aEndCommandPos - aStartCommandPos);
aCommandIndex++;
continue;
}
@ -1378,8 +1499,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
while(aCommand.Location(aTotalNbParams,COMMA,1,aCommand.Length()))
aTotalNbParams++;
if(SALOME::VerbosityActivated())
std::cout<<"aTotalNbParams = "<<aTotalNbParams<<std::endl;
MESSAGE("aTotalNbParams = " << aTotalNbParams);
Standard_Integer aFirstParam = aNbEntries;
@ -1420,15 +1540,13 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
if ( aStartPos == aEndPos )
continue; // PAL20889: for "[]"
if(SALOME::VerbosityActivated())
std::cout<<"aStartPos = "<<aStartPos<<", aEndPos = "<<aEndPos<<std::endl;
MESSAGE("aStartPos = " << aStartPos << ", aEndPos = " << aEndPos);
aVar = aCommand.SubString(aStartPos, aEndPos-1);
aVar.RightAdjust();
aVar.LeftAdjust();
if(SALOME::VerbosityActivated())
std::cout<<"Variable: '"<< aVar <<"'"<<std::endl;
MESSAGE("Variable: '" << aVar << "'");
// specific case for sketcher
if(aVar.Location( TCollection_AsciiString("Sketcher:"), 1, aVar.Length() ) != 0) {
@ -1447,8 +1565,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
aEndSectionPos = aVar.Length();
aSection = aVar.SubString(aStartSectionPos, aEndSectionPos-1);
if(SALOME::VerbosityActivated())
std::cout<<"aSection: "<<aSection<<std::endl;
MESSAGE("aSection: " << aSection);
Standard_Integer aNbParams = 1;
while( aSection.Location( aNbParams, ' ', 1, aSection.Length() ) )
@ -1464,15 +1581,13 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
else
aEndParamPos = aSection.Length() + 1;
if(SALOME::VerbosityActivated())
std::cout<<"aParamIndex: "<<aParamIndex<<" aStartParamPos: " <<aStartParamPos<<" aEndParamPos: "<<aEndParamPos<<std::endl;
MESSAGE("aParamIndex: " << aParamIndex << " aStartParamPos: " << aStartParamPos << " aEndParamPos: " << aEndParamPos);
if ( aStartParamPos == aEndParamPos)
continue;
aParameter = aSection.SubString(aStartParamPos, aEndParamPos-1);
if(SALOME::VerbosityActivated())
std::cout<<"aParameter: "<<aParameter<<std::endl;
MESSAGE("aParameter: " << aParameter);
if(iVar >= aVariables.size())
continue;
@ -1488,31 +1603,25 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
aReplacedParameter.InsertAfter(aReplacedParameter.Length(),"'");
}
if(SALOME::VerbosityActivated())
std::cout<<"aSection before : "<<aSection<< std::endl;
MESSAGE("aSection before : " << aSection);
aSection.Remove(aStartParamPos, aEndParamPos - aStartParamPos);
aSection.Insert(aStartParamPos, aReplacedParameter);
if(SALOME::VerbosityActivated())
std::cout<<"aSection after : "<<aSection<<std::endl<<std::endl;
MESSAGE("aSection after : " << aSection << '\n');
iVar++;
}
if(SALOME::VerbosityActivated())
std::cout<<"aVar before : "<<aVar<<std::endl;
MESSAGE("aVar before : " << aVar);
aVar.Remove(aStartSectionPos, aEndSectionPos - aStartSectionPos);
aVar.Insert(aStartSectionPos, aSection);
if(SALOME::VerbosityActivated())
std::cout<<"aVar after : "<<aVar<<std::endl<<std::endl;
MESSAGE("aVar after : " << aVar << '\n');
}
if(SALOME::VerbosityActivated())
std::cout<<"aCommand before : "<<aCommand<<std::endl;
MESSAGE("aCommand before : " << aCommand);
aCommand.Remove(aStartPos, aEndPos - aStartPos);
aCommand.Insert(aStartPos, aVar);
if(SALOME::VerbosityActivated())
std::cout<<"aCommand after : "<<aCommand<<std::endl;
MESSAGE("aCommand after : " << aCommand);
break;
} // end of specific case for sketcher
@ -1548,8 +1657,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
aStates->IncrementState();
}
if (SALOME::VerbosityActivated())
std::cout<<"Command : "<<theCommand<<std::endl;
MESSAGE("Command after replacing of the variables: " << theCommand);
}
//=============================================================================

View File

@ -1930,13 +1930,19 @@ bool GeometryGUI::activateModule( SUIT_Study* study )
PyErr_Print();
}
else {
PyObject* result =
PyObject_CallMethod(pluginsmanager, (char*)"initialize", (char*)"isss", 1, "geom",
tr("MEN_NEW_ENTITY").toUtf8().data(),
tr("GEOM_PLUGINS_OTHER").toUtf8().data());
auto addPluginsManager = [&](const char* managerName, const char* menuName, const char* submenuName) -> void
{
PyObject* result = PyObject_CallMethod(
pluginsmanager, (char*)"initialize", (char*)"isss", 1, managerName, menuName, submenuName);
if ( !result )
PyErr_Print();
Py_XDECREF(result);
};
addPluginsManager("geom", tr("MEN_NEW_ENTITY").toUtf8().data(), tr("GEOM_PLUGINS_OTHER").toUtf8().data());
addPluginsManager("geomrepairadv", tr("MEN_REPAIR").toUtf8().data(), tr("MEN_ADVANCED").toUtf8().data());
}
PyGILState_Release(gstate);
// end of GEOM plugins loading

View File

@ -1212,3 +1212,32 @@ Handle(GEOM_Object) GEOMImpl_IHealingOperations::LimitTolerance (Handle(GEOM_Obj
SetErrorCode(OK);
return aNewObject;
}
//=============================================================================
/*!
* FuncToPythonDump
*
* This function doesn't do any healing.
* Provides Python dump functionality for algorithms entirely implemented in Python.
*/
//=============================================================================
void GEOMImpl_IHealingOperations::FuncToPythonDump(
Handle(GEOM_Object) theObject,
Handle(GEOM_Object) result,
const char* imports,
const char* funcName,
const char* args
)
{
// Find a function to append a command
Handle(GEOM_Function) aFunction = result->GetLastFunction();
if (aFunction.IsNull())
{
MESSAGE("Can't get last function to append Python dump!");
return;
}
// Make a Python command - it will be appended to the given function
GEOM::TPythonDump pd (aFunction, true);
pd << imports << result << " = " << funcName << "(" << theObject << ", " << args << ")";
}

View File

@ -106,6 +106,17 @@ class GEOMImpl_IHealingOperations : public GEOM_IOperations {
double theTolerance,
TopAbs_ShapeEnum theType = TopAbs_SHAPE );
// This function doesn't do any healing.
// The only goal is to provide Python dump functionality for
// algorithms entirely implemented in Python.
void FuncToPythonDump(
Handle(GEOM_Object) theObject,
Handle(GEOM_Object) result,
const char* imports,
const char* funcName,
const char* args
);
const ShHealOper_ModifStats* GetStatistics() { return myModifStats; }
private:

View File

@ -343,6 +343,7 @@
#define SEWING_NON_MANIFOLD 11
#define REMOVE_INTERNAL_FACES 12
#define DIVIDE_EDGE_BY_POINT 13
#define FUNC_TO_PYTHON_DUMP 13
#define BASIC_FILLING 1
#define FILLING_ON_CONSTRAINTS 2

View File

@ -637,6 +637,26 @@ GEOM::GEOM_Object_ptr GEOM_IHealingOperations_i::LimitTolerance (GEOM::GEOM_Obje
return GetObject(aNewObject);
}
//=============================================================================
/*!
* LimitTolerance
*/
//=============================================================================
void GEOM_IHealingOperations_i::FuncToPythonDump (
GEOM::GEOM_Object_ptr theObject,
GEOM::GEOM_Object_ptr result,
const char* imports,
const char* funcName,
const char* args)
{
// Get the objects
Handle(::GEOM_Object) anObject = GetObjectImpl(theObject);
Handle(::GEOM_Object) aResult = GetObjectImpl(result);
// Perform
GetOperations()->FuncToPythonDump(anObject, aResult, imports, funcName, args);
}
//================================================================================
/*!
* \brief Return information on what has been done by the last called healing method

View File

@ -99,6 +99,16 @@ class GEOM_I_EXPORT GEOM_IHealingOperations_i :
GEOM::GEOM_Object_ptr LimitTolerance (GEOM::GEOM_Object_ptr theObject,
CORBA::Double theTolerance);
// The only goal is to provide Python dump functionality for
// algorithms entirely implemented in Python.
void FuncToPythonDump(
GEOM::GEOM_Object_ptr theObject,
GEOM::GEOM_Object_ptr result,
const char* imports,
const char* funcName,
const char* args
);
::GEOMImpl_IHealingOperations* GetOperations() { return (::GEOMImpl_IHealingOperations*)GetImpl(); }
GEOM::ModifStatistics* GetStatistics();

View File

@ -7483,6 +7483,26 @@ class geomBuilder(GEOM._objref_GEOM_Gen):
self._autoPublish(anObj, theName, "limitTolerance")
return anObj
## Provides Python dump functionality for algorithms entirely implemented in Python.
# @param theObject Shape to be processed.
# @param result Shape of the algorithm execution.
# @param imports module import for current functon..
# @param funcName name of a Python function that we need to put into dump.
# @param args arguments of the Python function.
#
def FuncToPythonDump(self, theObject, result, imports, funcName, args):
"""
Provides Python dump functionality for algorithms entirely implemented in Python.
Parameters:
theObject Shape to be processed.
result Shape of the algorithm execution.
imports module import for current functon.
funcName name of a Python function that we need to put into dump.
args arguments of the Python function.
"""
self.HealOp.FuncToPythonDump(theObject, result, imports, funcName, args)
## Get a list of wires (wrapped in GEOM.GEOM_Object-s),
# that constitute a free boundary of the given shape.
# @param theObject Shape to get free boundary of.

View File

@ -94,3 +94,27 @@ std::vector<int> GEOM_Swig_LocalSelector::getSelection()
return ids;
}
void GEOM_Swig_LocalSelector::setSelection(const std::vector<int> ids)
{
MESSAGE("setSelection() start...");
SalomeApp_Application* app = (SalomeApp_Application*)SUIT_Session::session()->activeApplication();
LightApp_SelectionMgr* aSelMgr = app->selectionMgr();
SALOME_ListIO aSelList;
aSelMgr->selectedObjects(aSelList);
MESSAGE("aSelList.Extent(): " << aSelList.Extent());
if (!aSelList.Extent())
{
return;
}
TColStd_IndexedMapOfInteger idsMap;
for (const auto i : ids)
{
idsMap.Add(i);
}
Handle(SALOME_InteractiveObject) anIO = aSelList.First();
aSelMgr->selectObjects(anIO, idsMap, false);
}

View File

@ -37,6 +37,7 @@ public:
~GEOM_Swig_LocalSelector();
std::vector<int> getSelection();
void setSelection(const std::vector<int> ids);
protected:
GEOM::GEOM_Object_var myObject;

View File

@ -66,6 +66,14 @@ GEOM_Swig::GEOM_Swig( bool updateOB )
*/
GEOM_Swig::~GEOM_Swig()
{
// Delete selector because of sigsegv in TEventInitLocalSelection::Execute()
// when call GEOM_Swig.initLocalSelection() from Python, close the study, create a new one and
// call it again.
if (myOCCSelector)
{
delete myOCCSelector;
myOCCSelector = nullptr;
}
}
/*!
@ -579,6 +587,33 @@ std::vector<int> GEOM_Swig::getLocalSelection()
return result;
}
/*!libGEOM_Swig
\brief Set local subShapes selection on a given shape
\param ids sub-shapes ids
*/
void GEOM_Swig::setLocalSelection(const std::vector<int> ids)
{
class TEventSetLocalSelection: public SALOME_Event
{
public:
typedef std::vector<int> TResult;
TResult myIds;
TResult myResult;
TEventSetLocalSelection(const std::vector<int> _ids) : myIds(_ids) {}
virtual void Execute()
{
MESSAGE("TEventSetLocalSelection myLocalSelector: " << myLocalSelector);
if (myLocalSelector)
myLocalSelector->setSelection(myIds);
}
};
ProcessEvent(new TEventSetLocalSelection(ids));
}
/*!
\brief close local subShapes selection on a given shape
*/

View File

@ -56,6 +56,7 @@ public:
GEOMGUI_EXPORT void initLocalSelection( const char* theEntry, int theMode);
GEOMGUI_EXPORT std::vector<int> getLocalSelection();
GEOMGUI_EXPORT void setLocalSelection(const std::vector<int> ids);
GEOMGUI_EXPORT void closeLocalSelection();
GEOMGUI_EXPORT int getIndexTopology( const char* theSubIOR, const char* theMainIOR );

View File

@ -83,6 +83,7 @@ class GEOM_Swig
void initLocalSelection( const char* theEntry, int theMode);
std::vector<int> getLocalSelection();
void setLocalSelection(const std::vector<int> ids);
void closeLocalSelection();
int getIndexTopology( const char* theSubIOR, const char* theMainIOR );