From: Jérôme Date: Thu, 8 Oct 2020 20:28:06 +0000 (+0200) Subject: functional version X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=d641c4b001c0f7326f1522250e29959eee6e28a4;p=modules%2Fshaper.git functional version --- diff --git a/src/BuildPlugin/BuildPlugin_EvalListener.cpp b/src/BuildPlugin/BuildPlugin_EvalListener.cpp new file mode 100644 index 000000000..73b4292a0 --- /dev/null +++ b/src/BuildPlugin/BuildPlugin_EvalListener.cpp @@ -0,0 +1,294 @@ +// Copyright (C) 2014-2020 CEA/DEN, EDF R&D +// +// 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, 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +//------------------------------------------------------------------------------ +// Tools + +std::wstring toString(double theValue) +{ + std::wostringstream sstream; + // write value in scientific format with 16 digits, + // thus, not check the dot position + sstream.precision(16); + sstream << std::scientific << theValue; + return sstream.str(); +} + +std::set toSet(const std::list& theContainer) +{ + return std::set(theContainer.begin(), theContainer.end()); +} + +//------------------------------------------------------------------------------ + +BuildPlugin_EvalListener::BuildPlugin_EvalListener() +{ + Events_Loop* aLoop = Events_Loop::loop(); + + aLoop->registerListener(this, ModelAPI_AttributeEvalMessage::eventId(), NULL, true); + aLoop->registerListener(this, ModelAPI_BuildEvalMessage::eventId(), NULL, true); + aLoop->registerListener(this, ModelAPI_ComputePositionsMessage::eventId(), NULL, true); + + myInterp = std::shared_ptr(new BuildPlugin_PyInterp()); + myInterp->initialize(); +} + +BuildPlugin_EvalListener::~BuildPlugin_EvalListener() +{ +} + +double BuildPlugin_EvalListener::evaluate(FeaturePtr theParameter, + const std::wstring& theExpression, std::string& theError, + std::list >& theParamsList, + const bool theIsParameter) +{ + std::list anExprParams = myInterp->compile(theExpression); + // find expression's params in the model + std::list aContext; + std::list::iterator it = anExprParams.begin(); + for ( ; it != anExprParams.end(); it++) { + double aValue; + ResultParameterPtr aParamRes; + // If variable does not exist python interpreter will generate an error. It is OK. + // But due to the issue 1479 it should not check the history position of parameters relatively + // to feature that contains expression + if (!ModelAPI_Tools::findVariable(theIsParameter ? theParameter : FeaturePtr(), + *it, aValue, aParamRes, theParameter->document())) + continue; + + if (theIsParameter) + theParamsList.push_back(aParamRes); + aContext.push_back(*it + L"=" + toString(aValue)); + } + myInterp->extendLocalContext(aContext); + double result = myInterp->evaluate(theExpression, theError); + myInterp->clearLocalContext(); + return result; +} + + +void BuildPlugin_EvalListener::processEvent( + const std::shared_ptr& theMessage) +{ + if (!theMessage.get()) + return; + + if (theMessage->eventID() == ModelAPI_AttributeEvalMessage::eventId()) { + processEvaluationEvent(theMessage); + } else if (theMessage->eventID() == ModelAPI_BuildEvalMessage::eventId()) { + std::shared_ptr aMsg = + std::dynamic_pointer_cast(theMessage); + FeaturePtr aParam = aMsg->parameter(); + + AttributeStringPtr anVariableAttr = aParam->string(BuildPlugin_Interpolation::VARIABLE_ID()); + std::wstring anVar = anVariableAttr->isUValue() ? + Locale::Convert::toWString(anVariableAttr->valueU()) : + Locale::Convert::toWString(anVariableAttr->value()); + AttributeTablesPtr anValueAttr = aParam->tables(BuildPlugin_Interpolation::VALUE_ID()); + std::string anError; + std::list > aParamsList; + + AttributeStringPtr anExprAttr; + ModelAPI_AttributeTables::Value aVal; + for(int step =0; step < anValueAttr->rows(); step++ ){ + anExprAttr = aParam->string(BuildPlugin_Interpolation::XT_ID()); + std::wstring anExp = anExprAttr->isUValue() ? + Locale::Convert::toWString(anExprAttr->valueU()) : + Locale::Convert::toWString(anExprAttr->value()); + aVal.myDouble = evaluate(anVar,anValueAttr->value(step,0).myDouble,aParam, anExp, anError, aParamsList, true); + anValueAttr->setValue(aVal,step,1); + + anExprAttr = aParam->string(BuildPlugin_Interpolation::YT_ID()); + anExp = anExprAttr->isUValue() ? + Locale::Convert::toWString(anExprAttr->valueU()) : + Locale::Convert::toWString(anExprAttr->value()); + aVal.myDouble = evaluate(anVar,anValueAttr->value(step,0).myDouble,aParam, anExp, anError, aParamsList, true); + anValueAttr->setValue(aVal,step,2); + + anExprAttr = aParam->string(BuildPlugin_Interpolation::ZT_ID()); + anExp = anExprAttr->isUValue() ? + Locale::Convert::toWString(anExprAttr->valueU()) : + Locale::Convert::toWString(anExprAttr->value()); + aVal.myDouble = evaluate(anVar,anValueAttr->value(step,0).myDouble,aParam, anExp, anError, aParamsList, true); + anValueAttr->setValue(aVal,step,3); + } + + aMsg->setResults(aParamsList, 1, anError); + } else if (theMessage->eventID() == ModelAPI_ComputePositionsMessage::eventId()) { + std::shared_ptr aMsg = + std::dynamic_pointer_cast(theMessage); + aMsg->setPositions(myInterp->positions(aMsg->expression(), aMsg->parameter())); + } +} + +double BuildPlugin_EvalListener::evaluate( + std::wstring& theVariable, + double theValueVariable, + FeaturePtr theParameter, + const std::wstring& theExpression, + std::string& theError, + std::list >& theParamsList, + const bool theIsParameter) +{ + std::list anExprParams = myInterp->compile(theExpression); + // find expression's params in the model + std::list aContext; + std::list::iterator it = anExprParams.begin(); + for ( ; it != anExprParams.end(); it++) { + double aValue; + ResultParameterPtr aParamRes; + // If variable does not exist python interpreter will generate an error. It is OK. + // But due to the issue 1479 it should not check the history position of parameters relatively + // to feature that contains expression + if (!ModelAPI_Tools::findVariable(theIsParameter ? theParameter : FeaturePtr(), + *it, aValue, aParamRes, theParameter->document())) + continue; + + if (theIsParameter) + theParamsList.push_back(aParamRes); + aContext.push_back(*it + L"=" + toString(aValue)); + } + aContext.push_back(theVariable + L"=" + toString(theValueVariable)); + myInterp->extendLocalContext(aContext); + double result = myInterp->evaluate(theExpression, theError); + myInterp->clearLocalContext(); + return result; +} + +void BuildPlugin_EvalListener::processEvaluationEvent( + const std::shared_ptr& theMessage) +{ + std::shared_ptr aMessage = + std::dynamic_pointer_cast(theMessage); + + std::list > aParamsList; + FeaturePtr aParamFeature = + std::dynamic_pointer_cast(aMessage->attribute()->owner()); + if (aMessage->attribute()->attributeType() == ModelAPI_AttributeInteger::typeId()) { + AttributeIntegerPtr anAttribute = + std::dynamic_pointer_cast(aMessage->attribute()); + std::string anError; + int aValue = (int)evaluate(aParamFeature, anAttribute->text(), anError, aParamsList); + bool isValid = anError.empty(); + if (isValid) + anAttribute->setCalculatedValue(aValue); + anAttribute->setUsedParameters(isValid ? + toSet(myInterp->compile(anAttribute->text())) : std::set()); + anAttribute->setExpressionInvalid(!isValid); + anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError); + } else + if (aMessage->attribute()->attributeType() == ModelAPI_AttributeDouble::typeId()) { + AttributeDoublePtr anAttribute = + std::dynamic_pointer_cast(aMessage->attribute()); + std::string anError; + double aValue = evaluate(aParamFeature, anAttribute->text(), anError, aParamsList); + bool isValid = anError.empty(); + if (isValid) + anAttribute->setCalculatedValue(aValue); + anAttribute->setUsedParameters(isValid ? + toSet(myInterp->compile(anAttribute->text())) : std::set()); + anAttribute->setExpressionInvalid(!isValid); + anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError); + } else + if (aMessage->attribute()->attributeType() == GeomDataAPI_Point::typeId()) { + AttributePointPtr anAttribute = + std::dynamic_pointer_cast(aMessage->attribute()); + std::wstring aText[] = { + anAttribute->textX(), + anAttribute->textY(), + anAttribute->textZ() + }; + double aCalculatedValue[] = { + anAttribute->x(), + anAttribute->y(), + anAttribute->z() + }; + for (int i = 0; i < 3; ++i) { + std::string anError; + double aValue = evaluate(aParamFeature, aText[i], anError, aParamsList); + bool isValid = anError.empty(); + if (isValid) aCalculatedValue[i] = aValue; + anAttribute->setUsedParameters(i, + isValid ? toSet(myInterp->compile(aText[i])) : std::set()); + anAttribute->setExpressionInvalid(i, !isValid); + anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError); + } + anAttribute->setCalculatedValue(aCalculatedValue[0], + aCalculatedValue[1], + aCalculatedValue[2]); + } else + if (aMessage->attribute()->attributeType() == GeomDataAPI_Point2D::typeId()) { + AttributePoint2DPtr anAttribute = + std::dynamic_pointer_cast(aMessage->attribute()); + std::wstring aText[] = { + anAttribute->textX(), + anAttribute->textY() + }; + double aCalculatedValue[] = { + anAttribute->x(), + anAttribute->y() + }; + for (int i = 0; i < 2; ++i) { + std::string anError; + double aValue = evaluate(aParamFeature, aText[i], anError, aParamsList); + bool isValid = anError.empty(); + if (isValid) aCalculatedValue[i] = aValue; + anAttribute->setUsedParameters(i, + isValid ? toSet(myInterp->compile(aText[i])) : std::set()); + anAttribute->setExpressionInvalid(i, !isValid); + anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError); + } + anAttribute->setCalculatedValue(aCalculatedValue[0], + aCalculatedValue[1]); + } +} + +void BuildPlugin_EvalListener::initDataModel() +{ + myInterp->runString("import salome_iapp;salome_iapp.register_module_in_study(\"Shaper\")"); +} diff --git a/src/BuildPlugin/BuildPlugin_EvalListener.h b/src/BuildPlugin/BuildPlugin_EvalListener.h new file mode 100644 index 000000000..3698e6f51 --- /dev/null +++ b/src/BuildPlugin/BuildPlugin_EvalListener.h @@ -0,0 +1,71 @@ +// Copyright (C) 2014-2020 CEA/DEN, EDF R&D +// +// 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, 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef SRC_BUILDPLUGIN_EVALLISTENER_H_ +#define SRC_BUILDPLUGIN_EVALLISTENER_H_ + +#include +#include + +class ModelAPI_Attribute; +class ModelAPI_Document; +class ModelAPI_Feature; +class ModelAPI_ResultParameter; +// JL_CGLB n'existe pas class InitializationPlugin_Parameter; +class BuildPlugin_PyInterp; + +/** + * \class BuildPlugin_EvalListener + * \ingroup Plugins + * \brief Class which process the events from the event loop. + */ +class BuildPlugin_EvalListener : public Events_Listener +{ + public: + BUILDPLUGIN_EXPORT BuildPlugin_EvalListener(); + BUILDPLUGIN_EXPORT virtual ~BuildPlugin_EvalListener(); + + /// Reimplemented from Events_Listener::processEvent(). + BUILDPLUGIN_EXPORT + virtual void processEvent(const std::shared_ptr& theMessage); + + // performs the python call to initialize high level data model on internal data model creation + void initDataModel(); + + protected: + /// Evaluates theExpression and returns its value. + double evaluate(std::wstring& theVariable, + double theValueVariable,std::shared_ptr theParameter, + const std::wstring& theExpression, std::string& theError, + std::list >& theParamsList, + const bool theIsParameter = false); + + double evaluate(std::shared_ptr theParameter, + const std::wstring& theExpression, std::string& theError, + std::list >& theParamsList, + const bool theIsParameter = false); + + /// Processes Evaluation event. + void processEvaluationEvent(const std::shared_ptr& theMessage); + + private: + std::shared_ptr myInterp; +}; + +#endif /* SRC_BUILDPLUGIN_EVALLISTENER_H_ */ diff --git a/src/BuildPlugin/BuildPlugin_Interpolation.cpp b/src/BuildPlugin/BuildPlugin_Interpolation.cpp index cffa63c57..ab0dec57a 100644 --- a/src/BuildPlugin/BuildPlugin_Interpolation.cpp +++ b/src/BuildPlugin/BuildPlugin_Interpolation.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -47,22 +48,18 @@ #include #include +#include + #include #include +#include -std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) { - size_t start_pos = 0; - while((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); // Handles case where 'to' is a substring of 'from' - } - return str; -} //================================================================================================= BuildPlugin_Interpolation::BuildPlugin_Interpolation() { + } //================================================================================================= @@ -82,6 +79,7 @@ void BuildPlugin_Interpolation::initAttributes() data()->addAttribute(EXPRESSION_ID(), ModelAPI_AttributeString::typeId()); data()->addAttribute(EXPRESSION_ERROR_ID(), ModelAPI_AttributeString::typeId()); data()->addAttribute(VARIABLE_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(VALUE_ID(), ModelAPI_AttributeTables::typeId()); data()->string(EXPRESSION_ERROR_ID())->setIsArgument(false); ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXPRESSION_ERROR_ID()); @@ -96,6 +94,7 @@ void BuildPlugin_Interpolation::initAttributes() ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), CREATION_METHODE_BY_SELECTION_ID()); ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXPRESSION_ID()); ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), VARIABLE_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), VALUE_ID()); data()->addAttribute(ARGUMENTS_ID(), ModelAPI_AttributeRefList::typeId()); data()->reflist(ARGUMENTS_ID())->setIsArgument(false); @@ -104,8 +103,44 @@ void BuildPlugin_Interpolation::initAttributes() } void BuildPlugin_Interpolation::attributeChanged(const std::string& theID) -{ +{ + if( (theID == XT_ID() + || theID == YT_ID() + || theID == ZT_ID() + || theID == MINT_ID() + || theID == MAXT_ID() + || theID == NUMSTEP_ID()) + && string(XT_ID())->value() !="" + && string(YT_ID())->value() !="" + && string(ZT_ID())->value() !="" + ){ + updateCoods(); + } + +} + +void BuildPlugin_Interpolation::updateCoods() +{ + std::wstring exp; + double aMint = real(MINT_ID())->value(); + double aMaxt = real(MAXT_ID())->value(); + int aNbrStep = integer(NUMSTEP_ID())->value(); + double scale = (aMaxt - aMint )/aNbrStep; + string(VARIABLE_ID())->setValue("t"); + tables(VALUE_ID())->setSize(aNbrStep,4); + for( int step = 0; step < aNbrStep; step++ ){ + ModelAPI_AttributeTables::Value aVal; + aVal.myDouble = step * scale + aMint; + tables(VALUE_ID())->setValue(aVal,step,0); + } + + outErrorMessage=""; + + evaluate(outErrorMessage); + if (!outErrorMessage.empty()){ + std::cout << L"outErrorMessage= " << outErrorMessage << std::endl; + } } //================================================================================================= @@ -196,148 +231,91 @@ void BuildPlugin_Interpolation::execute() }else { - double aMint = real(MINT_ID())->value(); - double aMaxt = real(MAXT_ID())->value(); - int aNbrStep = integer(NUMSTEP_ID())->value(); - double scale = (aMaxt - aMint )/aNbrStep; - std::cout << "aMint = " << aMint << std::endl; - std::cout << "aMaxt = " << aMaxt << std::endl; - std::cout << "aNbrStep = " << aNbrStep << std::endl; - std::cout << "scale = " << scale << std::endl; - + if( string( XT_ID())->value() == "" + ||string( YT_ID())->value() == "" + ||string( ZT_ID())->value() == "") + return; + + if (!outErrorMessage.empty()){ + setError("Error: Python interpreter " + outErrorMessage); + return; + } + AttributeTablesPtr table = tables( VALUE_ID() ); std::list > aCoodPoints; - for( int step = 0; step < aNbrStep; step++ ){ - std::vector coodPoint; - double result; - double value = step * scale + aMint; + for( int step = 0; step < table->rows() ; step++ ){ + std::vector coodPoint; + ModelAPI_AttributeTables::Value value; //x - std::string exp = string( XT_ID())->value(); - exp = ReplaceAll(exp,"t", std::to_string(value) ); - string(EXPRESSION_ID())->setValue(exp); - if (!updateExpression(result)) - setError("Expression error.", false); - coodPoint.push_back(result); + value = table->value(step, 1); + coodPoint.push_back( value.myDouble ); //y - exp = string( YT_ID())->value(); - exp = ReplaceAll(exp,"t", std::to_string(value) ); - string(EXPRESSION_ID())->setValue(exp); - if (!updateExpression(result)) - setError("Expression error.", false); - coodPoint.push_back(result); - //Z - exp = string( ZT_ID())->value(); - exp = ReplaceAll(exp,"t", std::to_string(value) ); - string(EXPRESSION_ID())->setValue(exp); - if (!updateExpression(result)) - setError("Expression error.", false); - coodPoint.push_back(result); + value = table->value(step, 2); + coodPoint.push_back( value.myDouble ); + // + value = table->value(step, 3); + coodPoint.push_back( value.myDouble ); aCoodPoints.push_back(coodPoint); } - + std::list aPoints; + std::list aVertices; std::list >::const_iterator aItCoodPoints = aCoodPoints.begin(); for( ; aItCoodPoints != aCoodPoints.end(); ++aItCoodPoints ){ std::cout << "cood = " << "(" << (*aItCoodPoints)[0] << ", "<< (*aItCoodPoints)[1] << ", "<< (*aItCoodPoints)[2] << " ) "<< std::endl; - std::shared_ptr vertex = + GeomVertexPtr vertex = GeomAlgoAPI_PointBuilder::vertex( (*aItCoodPoints)[0], (*aItCoodPoints)[1], (*aItCoodPoints)[2]); aPoints.push_back (vertex->point()); + aVertices.push_back (vertex); } + /* GeomDirPtr aDirStart(new GeomAPI_Dir( aCoodPoints.front()[0], aCoodPoints.front()[1], aCoodPoints.front()[2])); GeomDirPtr aDirEnd(new GeomAPI_Dir( aCoodPoints.back()[0], aCoodPoints.back()[1], - aCoodPoints.back()[2])); + aCoodPoints.back()[2]));*/ // Create curve from points GeomEdgePtr anEdge = - GeomAlgoAPI_CurveBuilder::edge(aPoints, false, true, aDirStart, aDirEnd); + GeomAlgoAPI_CurveBuilder::edge(aPoints, false, true,GeomDirPtr(),GeomDirPtr()); //aDirStart, aDirEnd); if (!anEdge.get()) { setError("Error: Result curve is empty."); return; } - - // Store result. + ResultBodyPtr aResultBody = document()->createBody(data()); - /*std::set::const_iterator aContextIt = aContexts.begin(); - for (; aContextIt != aContexts.end(); aContextIt++) { - aResultBody->storeModified(*aContextIt, anEdge, aContextIt == aContexts.begin()); - } - std::list::const_iterator aPointsIt = aPoints.begin(); - GeomAPI_ShapeExplorer anExp(anEdge, GeomAPI_Shape::EDGE); - for (; anExp.more() && aPointsIt != aPoints.cend(); anExp.next(), ++aPointsIt) { - GeomShapePtr aPoint = std::dynamic_pointer_cast( *aPointsIt ); - GeomShapePtr anEdge = anExp.current(); - aResultBody->generated(aPoint, anEdge); - }*/ + // Load the result + aResultBody->store(anEdge); int aVertexIndex = 1; for (GeomAPI_ShapeExplorer anExp(anEdge, GeomAPI_Shape::VERTEX); anExp.more(); anExp.next()) { std::string aVertexName = "Vertex_" + std::to_string((long long)aVertexIndex); aResultBody->generated(anExp.current(), aVertexName); + aVertexIndex++; } - + setResult(aResultBody); } } -double BuildPlugin_Interpolation::evaluate(const std::wstring& /*theExpression*/, - std::string& theError) +void BuildPlugin_Interpolation::evaluate(std::string& theError) { FeaturePtr aMyPtr = std::dynamic_pointer_cast(data()->owner()); - std::shared_ptr aProcessMessage = - ModelAPI_ParameterEvalMessage::send(aMyPtr, this); + std::shared_ptr aProcessMessage = + ModelAPI_BuildEvalMessage::send(aMyPtr, this); - double aResult = 0; if (aProcessMessage->isProcessed()) { - const std::list& aParamsList = aProcessMessage->params(); - aResult = aProcessMessage->result(); theError = aProcessMessage->error(); - // compare the list of parameters to store if changed - /* AttributeRefListPtr aParams = reflist(ARGUMENTS_ID()); - bool aDifferent = aParams->size() != (int)aParamsList.size(); - if (!aDifferent) { - std::list::const_iterator aNewIter = aParamsList.begin(); - std::list anOldList = aParams->list(); - std::list::const_iterator anOldIter = anOldList.begin(); - for(; !aDifferent && aNewIter != aParamsList.end(); aNewIter++, anOldIter++) { - if (*aNewIter != *anOldIter) - aDifferent = true; - } - } - if (aDifferent) { - aParams->clear(); - std::list::const_iterator aNewIter = aParamsList.begin(); - for(; aNewIter != aParamsList.end(); aNewIter++) { - aParams->append(*aNewIter); - } - }*/ + std::cout << "theError= " << theError << std::endl; } else { // error: python interpreter is not active theError = "Python interpreter is not available"; } - return aResult; } -bool BuildPlugin_Interpolation::updateExpression(double& result) -{ - std::wstring anExpression = string(EXPRESSION_ID())->isUValue() ? - Locale::Convert::toWString(string(EXPRESSION_ID())->valueU()) : - Locale::Convert::toWString(string(EXPRESSION_ID())->value()); - - std::string outErrorMessage; - result= evaluate(anExpression, outErrorMessage); - - data()->string(EXPRESSION_ERROR_ID())->setValue(outErrorMessage); - if (!outErrorMessage.empty()) - return false; - - std::cout << "result = " << result << std::endl; - return true; -} diff --git a/src/BuildPlugin/BuildPlugin_Interpolation.h b/src/BuildPlugin/BuildPlugin_Interpolation.h index 899819510..64181cb3b 100644 --- a/src/BuildPlugin/BuildPlugin_Interpolation.h +++ b/src/BuildPlugin/BuildPlugin_Interpolation.h @@ -141,6 +141,13 @@ public: static const std::string MY_VARIABLE_ID("variable"); return MY_VARIABLE_ID; } + + /// attribute of parameter name + inline static const std::string& VALUE_ID() + { + static const std::string MY_VALUE_ID("value"); + return MY_VALUE_ID; + } /// attribute of parameter expression inline static const std::string& EXPRESSION_ID() @@ -190,9 +197,12 @@ public: protected: /// Evaluates theExpression and returns its value. - double evaluate(const std::wstring& theExpression, std::string& theError); + void evaluate(std::string& theError); + + void updateCoods(); - bool updateExpression(double& result); + std::list aCoodPoints[3]; + std::string outErrorMessage; }; diff --git a/src/BuildPlugin/BuildPlugin_Plugin.cpp b/src/BuildPlugin/BuildPlugin_Plugin.cpp index 54ac6c7d0..45893e4c5 100644 --- a/src/BuildPlugin/BuildPlugin_Plugin.cpp +++ b/src/BuildPlugin/BuildPlugin_Plugin.cpp @@ -36,6 +36,9 @@ #include #include +#include + + // the only created instance of this plugin static BuildPlugin_Plugin* MY_INSTANCE = new BuildPlugin_Plugin(); @@ -62,9 +65,11 @@ BuildPlugin_Plugin::BuildPlugin_Plugin() aFactory->registerValidator("BuildPlugin_ValidatorExpressionInterpolation", new BuildPlugin_ValidatorExpressionInterpolation()); - // Register this plugin. ModelAPI_Session::get()->registerPlugin(this); + + myEvalListener = + std::shared_ptr(new BuildPlugin_EvalListener()); } //================================================================================================= diff --git a/src/BuildPlugin/BuildPlugin_Plugin.h b/src/BuildPlugin/BuildPlugin_Plugin.h index bafd51702..4f3f7f62a 100644 --- a/src/BuildPlugin/BuildPlugin_Plugin.h +++ b/src/BuildPlugin/BuildPlugin_Plugin.h @@ -25,6 +25,8 @@ #include #include +class BuildPlugin_EvalListener; + /// \class BuildPlugin_Plugin /// \ingroup Plugins /// \brief The main class for management the build features as plugin. @@ -36,6 +38,9 @@ public: /// Creates the feature object of this plugin by the feature string ID virtual FeaturePtr createFeature(std::string theFeatureID); + + private: + std::shared_ptr myEvalListener; }; #endif diff --git a/src/BuildPlugin/BuildPlugin_PyInterp.cpp b/src/BuildPlugin/BuildPlugin_PyInterp.cpp new file mode 100644 index 000000000..ae85fd5f6 --- /dev/null +++ b/src/BuildPlugin/BuildPlugin_PyInterp.cpp @@ -0,0 +1,268 @@ +// Copyright (C) 2014-2020 CEA/DEN, EDF R&D +// +// 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, 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include + +#include + +#include +#include +#include + +BuildPlugin_PyInterp::BuildPlugin_PyInterp() +: PyInterp_Interp() +{ +} + +BuildPlugin_PyInterp::~BuildPlugin_PyInterp() +{ +} + +const char* aSearchCode = + "import ast\n" + "class FindName(ast.NodeVisitor):\n" + " def __init__(self, name):\n" + " self.name = name\n" + " def visit_Name(self, node):\n" + " if node.id == self.name:\n" + " positions.append((node.lineno, node.col_offset))\n" + "FindName(name).visit(ast.parse(expression))"; + +// make the expression be correct for the python interpreter even for the +// beta=alfa*2 expressions +static std::wstring adjustExpression(const std::wstring& theExpression) { + std::wstring anExpression = theExpression; + if (!anExpression.empty() && anExpression.back() == L'=') { + anExpression = anExpression.substr(0, anExpression.length() - 1); + } + return anExpression; +} + +std::list > +BuildPlugin_PyInterp::positions(const std::wstring& theExpression, + const std::wstring& theName) +{ + PyLockWrapper lck; // Acquire GIL until the end of the method + + std::list > aResult; + + // prepare a context + PyObject* aContext = PyDict_New(); + PyDict_SetItemString(aContext, "__builtins__", PyEval_GetBuiltins()); + + std::wstring anExpression = adjustExpression(theExpression); + // extend aContext with variables + PyDict_SetItemString(aContext, "expression", + PyUnicode_FromWideChar(anExpression.c_str(), anExpression.size())); + PyDict_SetItemString(aContext, "name", PyUnicode_FromWideChar(theName.c_str(), theName.size())); + PyDict_SetItemString(aContext, "positions", Py_BuildValue("[]")); + + // run the search code + PyObject* aExecResult = PyRun_String(aSearchCode, Py_file_input, aContext, aContext); + Py_XDECREF(aExecResult); + + // receive results from context + PyObject* aPositions = PyDict_GetItemString(aContext, "positions"); + for (int anIndex = 0; anIndex < PyList_Size(aPositions); ++anIndex) { + PyObject* aPosition = PyList_GetItem(aPositions, anIndex); + PyObject* aLineNo = PyTuple_GetItem(aPosition, 0); + PyObject* aColOffset = PyTuple_GetItem(aPosition, 1); + + aResult.push_back( + std::pair((int)PyLong_AsLong(aLineNo), + (int)PyLong_AsLong(aColOffset))); + } + + // TODO(spo): after this refCount of the variable is not 0. Is there memory leak? + Py_DECREF(aContext); + + return aResult; +} + + +std::list BuildPlugin_PyInterp::compile(const std::wstring& theExpression) +{ + PyLockWrapper lck; // Acquire GIL until the end of the method + std::list aResult; + PyObject *aCodeopModule = PyImport_AddModule("codeop"); + if(!aCodeopModule) { // Fatal error. No way to go on. + PyErr_Print(); + return aResult; + } + // support "variable_name=" expression as "variable_name" + std::wstring anExpression = adjustExpression(theExpression); + + PyObject *aCodePyObj = + PyObject_CallMethod(aCodeopModule, (char*)"compile_command", (char*)"(s)", + Locale::Convert::toString(anExpression).c_str()); + + if(!aCodePyObj || aCodePyObj == Py_None || !PyCode_Check(aCodePyObj)) { + Py_XDECREF(aCodePyObj); + return aResult; + } + + PyCodeObject* aCodeObj = (PyCodeObject*) aCodePyObj; + std::string aCodeName(PyBytes_AsString(aCodeObj->co_code)); + // co_names should be tuple, but can be changed in modern versions of python (>2.7.3) + if(!PyTuple_Check(aCodeObj->co_names)) { + return aResult; + } + + size_t params_size = PyTuple_Size(aCodeObj->co_names); + if (params_size > 0) { + for (size_t i = 0; i < params_size; i++) { + PyObject* aParamObj = PyTuple_GetItem(aCodeObj->co_names, i); + PyObject* aParamObjStr = PyObject_Str(aParamObj); + Py_ssize_t aSize; + std::wstring aParamName(PyUnicode_AsWideCharString(aParamObjStr, &aSize)); + aResult.push_back(aParamName); + Py_XDECREF(aParamObjStr); + } + } + Py_XDECREF(aCodeObj); + return aResult; +} + +void BuildPlugin_PyInterp::extendLocalContext(const std::list& theParameters) +{ + PyLockWrapper lck; // Acquire GIL until the end of the method + if (theParameters.empty()) + return; + std::list::const_iterator it = theParameters.begin(); + for ( ; it != theParameters.cend(); it++) { + std::string aParamValue = Locale::Convert::toString(*it); + simpleRun(aParamValue.c_str(), false); + } +} + +void BuildPlugin_PyInterp::clearLocalContext() +{ + PyLockWrapper lck; + PyDict_Clear(_local_context); +} + +double BuildPlugin_PyInterp::evaluate(const std::wstring& theExpression, + std::string& theError) +{ + // support "variable_name=" expression as "variable_name" + std::wstring anExpression = adjustExpression(theExpression); + + PyLockWrapper lck; // Acquire GIL until the end of the method + PyCompilerFlags aFlags = {CO_FUTURE_DIVISION}; + aFlags.cf_flags = CO_FUTURE_DIVISION; + PyCodeObject* anExprCode = (PyCodeObject *) Py_CompileStringFlags( + Locale::Convert::toString(anExpression).c_str(), + "", Py_eval_input, &aFlags); + if(!anExprCode) { + theError = errorMessage(); + Py_XDECREF(anExprCode); + return 0.; + } + + PyObject* anEvalResult = PyEval_EvalCode((PyObject *)anExprCode, _global_context, _local_context); + if(!anEvalResult) { + theError = errorMessage(); + Py_XDECREF(anExprCode); + Py_XDECREF(anEvalResult); + return 0.; + } + + PyObject* anEvalStrObj = PyObject_Str(anEvalResult); + std::string anEvalStr(PyUnicode_AsUTF8(anEvalStrObj)); + Py_XDECREF(anExprCode); + Py_XDECREF(anEvalResult); + Py_XDECREF(anEvalStrObj); + double result = 0.; + try { + // set locale due to the #2485 + std::string aCurLocale = std::setlocale(LC_NUMERIC, 0); + std::setlocale(LC_NUMERIC, "C"); + result = std::stod(anEvalStr); + std::setlocale(LC_NUMERIC, aCurLocale.c_str()); + } + catch (const std::invalid_argument&) { + theError = "Unable to eval " + anEvalStr; + } + + return result; +} + +std::string BuildPlugin_PyInterp::errorMessage() +{ + std::string aPyError; + if (PyErr_Occurred()) { + PyObject *pstr, *ptype, *pvalue, *ptraceback; + PyErr_Fetch(&ptype, &pvalue, &ptraceback); + PyErr_NormalizeException(&ptype, &pvalue, &ptraceback); + pstr = PyObject_Str(pvalue); + aPyError = std::string(PyUnicode_AsUTF8(pstr)); + Py_XDECREF(pstr); + Py_XDECREF(ptype); + Py_XDECREF(pvalue); + Py_XDECREF(ptraceback); + } + return aPyError; +} + +bool BuildPlugin_PyInterp::initContext() +{ + PyObject *m = PyImport_AddModule("__main__"); // interpreter main module (module context) + if(!m){ + PyErr_Print(); + return false; + } + _global_context = PyModule_GetDict(m); // get interpreter global variable context + Py_INCREF(_global_context); + _local_context = PyDict_New(); + Py_INCREF(_local_context); + + // to avoid "help()" hang in the python console + const static char* aHelpTxt = "def help(): print(\"Available modules:\\n" + " salome.shaper.model : higher level access to features and data model\\n" + " BuildAPI : Build plugin features allowing to build shapes\\n" + " ConfigAPI : configuration management: preferences and XML properties\\n" + " ConstructionAPI : Construction plugin for auxiliary features creation\\n" + " EventsAPI : application events receiving and emitting manager\\n" + " ExchangeAPI : Exchange plugin with import/export features\\n" + " FeaturesAPI : Features plugin with general 3D features\\n" + " GeomAlgoAPI : geometrical algorithms\\n" + " GeomAPI : geometrical data structures\\n" + " GeomDataAPI : specific geometrical data structures stored in the data model\\n" + " ModelAPI : general low-level interface to access data model\\n" + " ModelHighAPI : general high-level interface to access data model\\n" + " ParametersAPI : Parameters plugin for parameters feature management\\n" + " PartSetAPI : PartSet plugin for management Parts features\\n" + " SketchAPI : Sketch plugin with all sketch features\")"; + + PyRun_SimpleString(aHelpTxt); + + return PyRun_SimpleString("from math import *") == 0; +} + +void BuildPlugin_PyInterp::closeContext() +{ + Py_XDECREF(_local_context); + PyInterp_Interp::closeContext(); +} + +bool BuildPlugin_PyInterp::runString(std::string theString) +{ + PyLockWrapper lck; // Acquire GIL until the end of the method + return PyRun_SimpleString(theString.c_str()); +} diff --git a/src/BuildPlugin/BuildPlugin_PyInterp.h b/src/BuildPlugin/BuildPlugin_PyInterp.h new file mode 100644 index 000000000..b5b8e14a8 --- /dev/null +++ b/src/BuildPlugin/BuildPlugin_PyInterp.h @@ -0,0 +1,65 @@ +// Copyright (C) 2014-2020 CEA/DEN, EDF R&D +// +// 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, 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef BUILDPLUGIN_PYINTERP_H_ +#define BUILDPLUGIN_PYINTERP_H_ + +#include +#include + +#include +#include +#include + +/** + * \class BuildPlugin_PyInterp + * \ingroup Plugins + * \brief Helper class for using Python interpreter. + */ +class BUILDPLUGIN_EXPORT BuildPlugin_PyInterp : public PyInterp_Interp +{ + public: + BuildPlugin_PyInterp(); + virtual ~BuildPlugin_PyInterp(); + + /// Returns a list of positions for theName in theExpression. + std::list > positions(const std::wstring& theExpression, + const std::wstring& theName); + /// Compiles theExpression and returns a list of parameters used in theExpression. + std::list compile(const std::wstring& theExpression); + /// Extends local context with the list of parameters. + void extendLocalContext(const std::list& theParameters); + /// Clears local context. + void clearLocalContext(); + /// Evaluates theExpression and returns its value. + double evaluate(const std::wstring& theExpression, std::string& theError); + + /// Runs the string command in the python interpreter. Returns true if no error is in result. + bool runString(std::string theString); + + protected: + /// Returns error message. + std::string errorMessage(); + /// Overrides PyInterp_Interp. + virtual bool initContext(); + /// Reimplemented from PyInterp_Interp::closeContext(). + virtual void closeContext(); +}; + +#endif /* BUILDPLUGIN_PYINTERP_H_ */ diff --git a/src/BuildPlugin/CMakeLists.txt b/src/BuildPlugin/CMakeLists.txt index e13730bea..929c321c6 100644 --- a/src/BuildPlugin/CMakeLists.txt +++ b/src/BuildPlugin/CMakeLists.txt @@ -29,6 +29,8 @@ INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/Events ${PROJECT_SOURCE_DIR}/src/GeomDataAPI ${PROJECT_SOURCE_DIR}/src/GeomValidators ${PROJECT_SOURCE_DIR}/src/SketchPlugin + ${SUIT_INCLUDE} + ${PYTHON_INCLUDE_DIR} ) SET(PROJECT_HEADERS @@ -48,6 +50,8 @@ SET(PROJECT_HEADERS BuildPlugin_SubShapes.h BuildPlugin_Filling.h BuildPlugin_Validators.h + BuildPlugin_EvalListener.h + BuildPlugin_PyInterp.h ) SET(PROJECT_SOURCES @@ -66,6 +70,8 @@ SET(PROJECT_SOURCES BuildPlugin_SubShapes.cpp BuildPlugin_Filling.cpp BuildPlugin_Validators.cpp + BuildPlugin_EvalListener.cpp + BuildPlugin_PyInterp.cpp ) SET(XML_RESOURCES @@ -99,6 +105,8 @@ SET(PROJECT_LIBRARIES GeomAPI GeomAlgoAPI GeomValidators + ${PyInterp} + ${PYTHON_LIBRARIES} ) ADD_DEFINITIONS(-DBUILDPLUGIN_EXPORTS) diff --git a/src/BuildPlugin/icons/feature_interpolation_analytical.png.png b/src/BuildPlugin/icons/feature_interpolation_analytical.png.png new file mode 100644 index 000000000..e5c5374b9 Binary files /dev/null and b/src/BuildPlugin/icons/feature_interpolation_analytical.png.png differ diff --git a/src/BuildPlugin/icons/feature_interpolation_by_selection.png b/src/BuildPlugin/icons/feature_interpolation_by_selection.png new file mode 100644 index 000000000..f2964c744 Binary files /dev/null and b/src/BuildPlugin/icons/feature_interpolation_by_selection.png differ diff --git a/src/BuildPlugin/interpolation_widget.xml b/src/BuildPlugin/interpolation_widget.xml index 10efe19e7..cd35b1522 100644 --- a/src/BuildPlugin/interpolation_widget.xml +++ b/src/BuildPlugin/interpolation_widget.xml @@ -1,7 +1,8 @@ + title="Curve by selection" + icon="icons/Build/feature_interpolation_by_selection.png"> - + + icon="icons/Build/feature_interpolation_analytical.png"> @@ -50,13 +51,13 @@ label="Min t" min="0" default="0"> - + - + + helpfile="interpolationFeature.html"> >& theParamsList, + const double theResult, const std::string& theError) +{ + myParamsList = theParamsList; + myResult = theResult; + myError = theError; + myIsProcessed = true; +} + +bool ModelAPI_BuildEvalMessage::isProcessed() +{ + return myIsProcessed; +} + +const std::list >& + ModelAPI_BuildEvalMessage::params() const +{ + return myParamsList; +} + +const double& ModelAPI_BuildEvalMessage::result() const +{ + return myResult; +} + +const std::string& ModelAPI_BuildEvalMessage::error() const +{ + return myError; +} + ModelAPI_ComputePositionsMessage::ModelAPI_ComputePositionsMessage( const Events_ID theID, const void* theSender) : Events_Message(theID, theSender) diff --git a/src/ModelAPI/ModelAPI_Events.h b/src/ModelAPI/ModelAPI_Events.h index fccf6e240..11a6aecc9 100644 --- a/src/ModelAPI/ModelAPI_Events.h +++ b/src/ModelAPI/ModelAPI_Events.h @@ -352,6 +352,59 @@ class ModelAPI_ParameterEvalMessage : public Events_Message MODELAPI_EXPORT const std::string& error() const; }; +class ModelAPI_BuildEvalMessage : public Events_Message +{ + FeaturePtr myParam; ///< parameters that should be evaluated + bool myIsProcessed; ///< true if results were set + /// result of processing, list of parameters in expression found + std::list > myParamsList; + double myResult; ///< result of processing, the computed value of the expression + std::string myError; ///< error of processing, empty if there is no error + + public: + /// Static. Returns EventID of the message. + MODELAPI_EXPORT static Events_ID& eventId() + { + static const char * MY_BUILD_EVALUATION_EVENT_ID("ParameterEvaluationRequest"); + static Events_ID anId = Events_Loop::eventByName(MY_BUILD_EVALUATION_EVENT_ID); + return anId; + } + + /// Useful method that creates and sends the event. + /// Returns the message, processed, with the resulting fields filled. + MODELAPI_EXPORT static std::shared_ptr + send(FeaturePtr theParameter, const void* theSender) + { + std::shared_ptr aMessage = + std::shared_ptr( + new ModelAPI_BuildEvalMessage(eventId(), theSender)); + aMessage->setParameter(theParameter); + Events_Loop::loop()->send(aMessage); + return aMessage; + } + + /// Creates an empty message + MODELAPI_EXPORT ModelAPI_BuildEvalMessage(const Events_ID theID, const void* theSender = 0); + /// The virtual destructor + MODELAPI_EXPORT virtual ~ModelAPI_BuildEvalMessage(); + + /// Returns a parameter stored in the message + MODELAPI_EXPORT FeaturePtr parameter() const; + /// Sets a parameter to the message + MODELAPI_EXPORT void setParameter(FeaturePtr theParam); + /// Sets the results of processing + MODELAPI_EXPORT void setResults( + const std::list >& theParamsList, + const double theResult, const std::string& theError); + /// Returns true if the expression is processed + MODELAPI_EXPORT bool isProcessed(); + /// Returns the results of processing: list of parameters found in the expression + MODELAPI_EXPORT const std::list >& params() const; + /// Returns the expression result + MODELAPI_EXPORT const double& result() const; + /// Returns the interpreter error (empty if no error) + MODELAPI_EXPORT const std::string& error() const; +}; /// Message to ask compute the positions of parameters in the expression class ModelAPI_ComputePositionsMessage : public Events_Message