From 8e643ca0a81cd0d2a306efac509016e84b5282d9 Mon Sep 17 00:00:00 2001 From: cg246364 Date: Fri, 19 Feb 2021 18:29:05 +0100 Subject: [PATCH] CEA : Lot1 - Interpolation --- src/BuildAPI/BuildAPI_Interpolation.cpp | 102 ++++-- src/BuildAPI/BuildAPI_Interpolation.h | 41 ++- src/BuildPlugin/BuildPlugin_Interpolation.cpp | 301 ++++++++++++++---- src/BuildPlugin/BuildPlugin_Interpolation.h | 106 ++++++ src/BuildPlugin/BuildPlugin_Plugin.cpp | 2 + src/BuildPlugin/BuildPlugin_Validators.cpp | 28 ++ src/BuildPlugin/BuildPlugin_Validators.h | 15 + src/BuildPlugin/BuildPlugin_msg_fr.ts | 36 ++- src/BuildPlugin/CMakeLists.txt | 6 + src/BuildPlugin/Test/TestInterpolation.py | 12 +- .../doc/TUI_interpolationAnalytical.rst | 12 + ...e.rst => TUI_interpolationBySelection.rst} | 6 +- .../doc/examples/interpolationAnalytical.py | 9 + .../images/CreateInterpolationAnalytical.png | Bin 0 -> 14456 bytes src/BuildPlugin/doc/images/Interpolation.png | Bin 8000 -> 11659 bytes .../doc/images/InterpolationAnalytical.png | Bin 0 -> 14371 bytes .../feature_interpolation_analytical.png | Bin 0 -> 1172 bytes .../feature_interpolation_by_selection.png | Bin 0 -> 511 bytes src/BuildPlugin/doc/interpolationFeature.rst | 76 ++++- .../feature_interpolation_analytical.png | Bin 0 -> 1172 bytes .../feature_interpolation_by_selection.png | Bin 0 -> 511 bytes src/BuildPlugin/interpolation_widget.xml | 95 ++++-- src/GeomValidators/CMakeLists.txt | 1 + .../GeomValidators_MinObjectsSelected.cpp | 17 +- src/InitializationPlugin/CMakeLists.txt | 1 + .../InitializationPlugin_EvalListener.cpp | 136 +++++++- .../InitializationPlugin_EvalListener.h | 12 +- src/ModelAPI/ModelAPI_Events.cpp | 43 +++ src/ModelAPI/ModelAPI_Events.h | 51 +++ 29 files changed, 979 insertions(+), 129 deletions(-) create mode 100644 src/BuildPlugin/doc/TUI_interpolationAnalytical.rst rename src/BuildPlugin/doc/{TUI_interpolationFeature.rst => TUI_interpolationBySelection.rst} (57%) create mode 100644 src/BuildPlugin/doc/examples/interpolationAnalytical.py create mode 100644 src/BuildPlugin/doc/images/CreateInterpolationAnalytical.png create mode 100644 src/BuildPlugin/doc/images/InterpolationAnalytical.png create mode 100644 src/BuildPlugin/doc/images/feature_interpolation_analytical.png create mode 100644 src/BuildPlugin/doc/images/feature_interpolation_by_selection.png create mode 100644 src/BuildPlugin/icons/feature_interpolation_analytical.png create mode 100644 src/BuildPlugin/icons/feature_interpolation_by_selection.png diff --git a/src/BuildAPI/BuildAPI_Interpolation.cpp b/src/BuildAPI/BuildAPI_Interpolation.cpp index 113ed4227..8f1efda8c 100644 --- a/src/BuildAPI/BuildAPI_Interpolation.cpp +++ b/src/BuildAPI/BuildAPI_Interpolation.cpp @@ -38,7 +38,8 @@ BuildAPI_Interpolation::BuildAPI_Interpolation(const FeaturePtr& theFeature, const bool theIsToReorder) : ModelHighAPI_Interface(theFeature) { - if(initialize()) { + if (initialize()) { + fillAttribute(BuildPlugin_Interpolation::CREATION_METHOD_BY_SELECTION_ID(),mycreationmethod); setUseTangents(true); setTangents(theStartTangent, theEndTangent); setClosed(theIsClosed); @@ -55,6 +56,7 @@ BuildAPI_Interpolation::BuildAPI_Interpolation(const FeaturePtr& theFeature, : ModelHighAPI_Interface(theFeature) { if (initialize()) { + fillAttribute(BuildPlugin_Interpolation::CREATION_METHOD_BY_SELECTION_ID(),mycreationmethod); setClosed(theIsClosed); setReorder(theIsToReorder); setUseTangents(false); @@ -62,6 +64,29 @@ BuildAPI_Interpolation::BuildAPI_Interpolation(const FeaturePtr& theFeature, } } +//================================================================================================== +BuildAPI_Interpolation::BuildAPI_Interpolation(const FeaturePtr& theFeature, + const std::string & theXTexpression, + const std::string & theYTexpression, + const std::string & theZTexpression, + const ModelHighAPI_Double& theMinT, + const ModelHighAPI_Double& theMaxT, + const ModelHighAPI_Integer& theNbStep) +: ModelHighAPI_Interface(theFeature) +{ + if (initialize()) { + fillAttribute(BuildPlugin_Interpolation::CREATION_METHOD_ANALYTICAL_ID(),mycreationmethod); + fillAttribute(theXTexpression, myxt); + fillAttribute(theYTexpression, myyt); + fillAttribute(theZTexpression, myzt); + fillAttribute(theMinT, mymint); + fillAttribute(theMaxT, mymaxt); + fillAttribute(theNbStep, mynumstep); + + execute(); + } +} + //================================================================================================== BuildAPI_Interpolation::~BuildAPI_Interpolation() { @@ -112,24 +137,45 @@ void BuildAPI_Interpolation::dump(ModelHighAPI_Dumper& theDumper) const FeaturePtr aBase = feature(); std::string aPartName = theDumper.name(aBase->document()); - AttributeSelectionListPtr anAttrBaseObjects = - aBase->selectionList(BuildPlugin_Interpolation::BASE_OBJECTS_ID()); - - theDumper << aBase << " = model.addInterpolation(" << aPartName << ", " - << anAttrBaseObjects << ", "; - - AttributeStringPtr useTangentsAttr = useTangents(); - std::string useTangents = useTangentsAttr->value(); - if (!useTangents.empty()) { - AttributeSelectionPtr anAttrStartTangent = - aBase->selection(BuildPlugin_Interpolation::TANGENT_START_ID()); - AttributeSelectionPtr anAttrEndTangent = - aBase->selection(BuildPlugin_Interpolation::TANGENT_END_ID()); - - theDumper << anAttrStartTangent << ", " << anAttrEndTangent << ", "; + if (aBase->string(BuildPlugin_Interpolation::CREATION_METHOD_ID())->value() == + BuildPlugin_Interpolation::CREATION_METHOD_BY_SELECTION_ID()) + { + AttributeSelectionListPtr anAttrBaseObjects = + aBase->selectionList(BuildPlugin_Interpolation::BASE_OBJECTS_ID()); + + theDumper << aBase << " = model.addInterpolation(" << aPartName << ", " + << anAttrBaseObjects << ", "; + + AttributeStringPtr useTangentsAttr = useTangents(); + std::string useTangents = useTangentsAttr->value(); + if (!useTangents.empty()) { + AttributeSelectionPtr anAttrStartTangent = + aBase->selection(BuildPlugin_Interpolation::TANGENT_START_ID()); + AttributeSelectionPtr anAttrEndTangent = + aBase->selection(BuildPlugin_Interpolation::TANGENT_END_ID()); + + theDumper << anAttrStartTangent << ", " << anAttrEndTangent << ", "; + } + + theDumper << closed() << ", " << reorder() << ")" << std::endl; + } else { + theDumper << aBase << " = model.addInterpolation(" << aPartName ; + AttributeStringPtr XtAttr = xt(); + std::string xt = XtAttr->value(); + AttributeStringPtr YtAttr = yt(); + std::string yt = YtAttr->value(); + AttributeStringPtr ZtAttr = zt(); + std::string zt = ZtAttr->value(); + AttributeDoublePtr minTAttr = mint(); + double mint = minTAttr->value(); + AttributeDoublePtr maxTAttr = maxt(); + double maxt = maxTAttr->value(); + AttributeIntegerPtr nbStepAttr = numstep(); + int nbStep = nbStepAttr->value(); + + theDumper<< ", \"" << xt << "\",\"" << yt << "\",\""<< zt<< "\", " ; + theDumper << mint << ", " << maxt << ", "<< nbStep<< ")"<& theP theIsToReorder)); } +InterpolationPtr addInterpolation(const std::shared_ptr& thePart, + const std::string & theXTexpression, + const std::string & theYTexpression, + const std::string & theZTexpression, + const ModelHighAPI_Double& theMinT, + const ModelHighAPI_Double& theMaxT, + const ModelHighAPI_Integer& theNbStep) +{ + std::shared_ptr aFeature = thePart->addFeature(BuildAPI_Interpolation::ID()); + return InterpolationPtr(new BuildAPI_Interpolation(aFeature, + theXTexpression, + theYTexpression, + theZTexpression, + theMinT, + theMaxT, + theNbStep)); +} + //================================================================================================== void BuildAPI_Interpolation::execIfBaseNotEmpty() { if (baseObjects()->size() > 0) execute(); -} \ No newline at end of file +} diff --git a/src/BuildAPI/BuildAPI_Interpolation.h b/src/BuildAPI/BuildAPI_Interpolation.h index e1ee1031d..86c511f0c 100644 --- a/src/BuildAPI/BuildAPI_Interpolation.h +++ b/src/BuildAPI/BuildAPI_Interpolation.h @@ -24,6 +24,8 @@ #include +#include +#include #include #include #include @@ -54,11 +56,21 @@ public: const std::list& theBaseObjects, const bool theIsClosed, const bool theIsToReorder); + /// Constructor with expression analytical of X,Y andZ + BUILDAPI_EXPORT + explicit BuildAPI_Interpolation(const FeaturePtr& theFeature, + const std::string & theXTexpression, + const std::string & theYTexpression, + const std::string & theZTexpression, + const ModelHighAPI_Double& theMinT, + const ModelHighAPI_Double& theMaxT, + const ModelHighAPI_Integer& theNbStep); + /// Destructor. BUILDAPI_EXPORT virtual ~BuildAPI_Interpolation(); - INTERFACE_6(BuildPlugin_Interpolation::ID(), + INTERFACE_13(BuildPlugin_Interpolation::ID(), baseObjects, BuildPlugin_Interpolation::BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList, /** Base objects */, closed, BuildPlugin_Interpolation::CLOSED_ID(), @@ -70,7 +82,21 @@ public: startTangent, BuildPlugin_Interpolation::TANGENT_START_ID(), ModelAPI_AttributeSelection, /** Start point tangent */, endTangent, BuildPlugin_Interpolation::TANGENT_END_ID(), - ModelAPI_AttributeSelection, /** End point tangent */) + ModelAPI_AttributeSelection, /** End point tangent */, + xt, BuildPlugin_Interpolation::XT_ID(), + ModelAPI_AttributeString, /** xt expression*/, + yt, BuildPlugin_Interpolation::YT_ID(), + ModelAPI_AttributeString, /** yt expression*/, + zt, BuildPlugin_Interpolation::ZT_ID(), + ModelAPI_AttributeString, /** zt expression*/, + mint, BuildPlugin_Interpolation::MINT_ID(), + ModelAPI_AttributeDouble, /** Min*/, + maxt, BuildPlugin_Interpolation::MAXT_ID(), + ModelAPI_AttributeDouble, /** Max*/, + numstep, BuildPlugin_Interpolation::NUMSTEP_ID(), + ModelAPI_AttributeInteger, /** Number of steps*/, + creationmethod, BuildPlugin_Interpolation::CREATION_METHOD_ID(), + ModelAPI_AttributeString, /** Creation method*/) /// Modify base attribute of the feature. BUILDAPI_EXPORT @@ -118,4 +144,15 @@ InterpolationPtr addInterpolation(const std::shared_ptr& theP const bool theIsClosed = false, const bool theIsToReorder = false); +/// \ingroup CPPHighAPI +/// \brief Create Interpolation feature using tangents. +BUILDAPI_EXPORT +InterpolationPtr addInterpolation(const std::shared_ptr& thePart, + const std::string & theXTexpression, + const std::string & theYTexpression, + const std::string & theZTexpression, + const ModelHighAPI_Double& theMinT, + const ModelHighAPI_Double& theMaxT, + const ModelHighAPI_Integer& theNbStep); + #endif // BuildAPI_Interpolation_H_ diff --git a/src/BuildPlugin/BuildPlugin_Interpolation.cpp b/src/BuildPlugin/BuildPlugin_Interpolation.cpp index b8422517b..bb8266db0 100644 --- a/src/BuildPlugin/BuildPlugin_Interpolation.cpp +++ b/src/BuildPlugin/BuildPlugin_Interpolation.cpp @@ -20,13 +20,22 @@ #include "BuildPlugin_Interpolation.h" #include -#include +#include +#include +#include #include +#include +#include +#include #include -#include +#include +#include +#include #include +#include + #include #include #include @@ -36,6 +45,7 @@ #include #include +#include //================================================================================================= BuildPlugin_Interpolation::BuildPlugin_Interpolation() @@ -51,6 +61,92 @@ void BuildPlugin_Interpolation::initAttributes() data()->addAttribute(USE_TANGENTS_ID(), ModelAPI_AttributeString::typeId()); data()->addAttribute(TANGENT_START_ID(), ModelAPI_AttributeSelection::typeId()); data()->addAttribute(TANGENT_END_ID(), ModelAPI_AttributeSelection::typeId()); + + data()->addAttribute(CREATION_METHOD_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(CREATION_METHOD_BY_SELECTION_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(CREATION_METHOD_ANALYTICAL_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()); + + data()->addAttribute(XT_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(YT_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(ZT_ID(), ModelAPI_AttributeString::typeId()); + data()->addAttribute(MINT_ID(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(MAXT_ID(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(NUMSTEP_ID(), ModelAPI_AttributeInteger::typeId()); + + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), + CREATION_METHOD_ANALYTICAL_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), + CREATION_METHOD_BY_SELECTION_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); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), ARGUMENTS_ID()); + + if (string(XT_ID())->value() == "" + && string(YT_ID())->value() == "" + && string(ZT_ID())->value() == "") { + string(XT_ID())->setValue("t"); + string(YT_ID())->setValue("t"); + string(ZT_ID())->setValue("t"); + real(MINT_ID())->setValue(0); + real(MAXT_ID())->setValue(100); + integer(NUMSTEP_ID())->setValue(10); + updateCoordinates(); + } +} + +//================================================================================================= +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() !="" + && string(CREATION_METHOD_ID())->value() == CREATION_METHOD_ANALYTICAL_ID()) { + updateCoordinates(); + } +} + +//================================================================================================= +void BuildPlugin_Interpolation::updateCoordinates() +{ + std::wstring exp; + double aMint = real(MINT_ID())->value(); + double aMaxt = real(MAXT_ID())->value(); + int aNbrStep = integer(NUMSTEP_ID())->value(); + + if (aMaxt < aMint) { + setError("The minimum value of the parameter must be less than maximum value !!!"); + } + + double aScale = (aMaxt - aMint)/aNbrStep; + string(VARIABLE_ID())->setValue("t"); + + tables(VALUE_ID())->setSize(aNbrStep+1,4); + for (int step = 0; step <= aNbrStep; step++) { + ModelAPI_AttributeTables::Value aVal; + aVal.myDouble = step * aScale + aMint; + tables(VALUE_ID())->setValue(aVal,step,0); + } + + outErrorMessage=""; + evaluate(outErrorMessage); + data()->string(EXPRESSION_ERROR_ID())->setValue(outErrorMessage); + if (!outErrorMessage.empty()) { + setError("Error: Python interpreter "); + return; + } } //================================================================================================= @@ -78,62 +174,159 @@ static GeomDirPtr selectionToDir(const AttributeSelectionPtr& theSelection) //================================================================================================= void BuildPlugin_Interpolation::execute() { - // Get closed flag value - bool isClosed = boolean(CLOSED_ID())->value(); + if (string(CREATION_METHOD_ID())->value() == CREATION_METHOD_BY_SELECTION_ID()) { + // Get closed flag value + bool isClosed = boolean(CLOSED_ID())->value(); - // Get reorder flag value - bool isToReorder = boolean(REORDER_ID())->value(); + // Get reorder flag value + bool isToReorder = boolean(REORDER_ID())->value(); - // Get use tangents flag value - bool isToUseTangents = isClosed? false : (!string(USE_TANGENTS_ID())->value().empty()); + // Get use tangents flag value + bool isToUseTangents = isClosed? false : (!string(USE_TANGENTS_ID())->value().empty()); - // Get tangent for start and end points - GeomDirPtr aDirStart, aDirEnd; - if (isToUseTangents) { - aDirStart = selectionToDir(selection(TANGENT_START_ID())); - aDirEnd = selectionToDir(selection(TANGENT_END_ID())); - } + // Get tangent for start and end points + GeomDirPtr aDirStart, aDirEnd; + if (isToUseTangents) { + aDirStart = selectionToDir(selection(TANGENT_START_ID())); + aDirEnd = selectionToDir(selection(TANGENT_END_ID())); + } - // Get base objects list. - AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID()); - - // Collect points. - std::list aPoints; - std::set aContexts; - for (int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) { - AttributeSelectionPtr aSelection = aSelectionList->value(anIndex); - - GeomShapePtr aContextShape = aSelection->context()->shape(); - aContexts.insert(aContextShape); - - GeomShapePtr aShape = aSelection->value(); - if (!aShape.get()) { - aShape = aContextShape; - } - - GeomPointPtr aPoint = GeomAlgoAPI_PointBuilder::point(aShape); - aPoints.push_back(aPoint); - } - - // Create curve from points - GeomEdgePtr anEdge = - GeomAlgoAPI_CurveBuilder::edge(aPoints, isClosed, isToReorder, aDirStart, aDirEnd); - if (!anEdge.get()) { - setError("Error: Result curve is empty."); - return; - } + // Get base objects list. + AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID()); - // 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()); - } - 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); - } + // Collect points. + std::list aPoints; + std::set aContexts; + for (int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) { + AttributeSelectionPtr aSelection = aSelectionList->value(anIndex); + + GeomShapePtr aContextShape = aSelection->context()->shape(); + aContexts.insert(aContextShape); + + GeomShapePtr aShape = aSelection->value(); + if (!aShape.get()) { + aShape = aContextShape; + } + + GeomPointPtr aPoint = GeomAlgoAPI_PointBuilder::point(aShape); + aPoints.push_back(aPoint); + } + + // Create curve from points + GeomEdgePtr anEdge = + GeomAlgoAPI_CurveBuilder::edge(aPoints, isClosed, isToReorder, 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()); + } + 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); + } + + setResult(aResultBody); + + } else { + if (string(XT_ID())->value() == "" + ||string(YT_ID())->value() == "" + ||string(ZT_ID())->value() == "" + ||tables(VALUE_ID())->rows()== 0) + return; + + bool aWasBlocked = data()->blockSendAttributeUpdated(true); + updateCoordinates(); + data()->blockSendAttributeUpdated(aWasBlocked, false); + + AttributeTablesPtr aTable = tables(VALUE_ID()); + std::list> aCoordPoints; + for (int step = 0; step < aTable->rows(); step++) { + std::vector aCoordPoint; + ModelAPI_AttributeTables::Value aValue; + //x + aValue = aTable->value(step, 1); + aCoordPoint.push_back(aValue.myDouble); + //y + aValue = aTable->value(step, 2); + aCoordPoint.push_back(aValue.myDouble); + // + aValue = aTable->value(step, 3); + aCoordPoint.push_back(aValue.myDouble); + + aCoordPoints.push_back(aCoordPoint); + } + + std::list aPoints; + std::list>::const_iterator anItCoordPoints = aCoordPoints.begin(); - setResult(aResultBody); + for (; anItCoordPoints!=aCoordPoints.end(); ++anItCoordPoints) { + + GeomVertexPtr aVertex = GeomAlgoAPI_PointBuilder::vertex((*anItCoordPoints)[0], + (*anItCoordPoints)[1], + (*anItCoordPoints)[2]); + aPoints.push_back(aVertex->point()); + } + + // test if some points are identical + std::list::const_iterator anItPoint1 = aPoints.begin(); + std::list::const_iterator anItPoint2; + for(; anItPoint1 != aPoints.end(); ++ anItPoint1) { + anItPoint2 = anItPoint1; + ++anItPoint2; + for(; anItPoint2 != aPoints.end(); ++ anItPoint2) + if ((*anItPoint2)->isEqual(*anItPoint1)) { + setError("Error: Several points are identical"); + return; + } + } + + // Create curve from points + GeomEdgePtr anEdge = + GeomAlgoAPI_CurveBuilder::edge(aPoints, false, false,GeomDirPtr(),GeomDirPtr()); + if (!anEdge.get()) { + setError("Error: Result curve is empty."); + return; + } + + ResultBodyPtr aResultBody = document()->createBody(data()); + // 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); + } } + +//================================================================================================= +void BuildPlugin_Interpolation::evaluate(std::string& theError) +{ + FeaturePtr aMyPtr = std::dynamic_pointer_cast(data()->owner()); + std::shared_ptr aProcessMessage = + ModelAPI_BuildEvalMessage::send(aMyPtr, this); + + if (aProcessMessage->isProcessed()) { + theError = aProcessMessage->error(); + + const std::list& aParamsList = aProcessMessage->params(); + //store the list of parameters to store if changed + AttributeRefListPtr aParams = reflist(ARGUMENTS_ID()); + aParams->clear(); + std::list::const_iterator aNewIter = aParamsList.begin(); + for (; aNewIter != aParamsList.end(); aNewIter++) { + aParams->append(*aNewIter); + } + } else { // error: python interpreter is not active + theError = "Python interpreter is not available"; + } +} \ No newline at end of file diff --git a/src/BuildPlugin/BuildPlugin_Interpolation.h b/src/BuildPlugin/BuildPlugin_Interpolation.h index 912cc126f..624d584f1 100644 --- a/src/BuildPlugin/BuildPlugin_Interpolation.h +++ b/src/BuildPlugin/BuildPlugin_Interpolation.h @@ -40,6 +40,27 @@ public: return MY_ID; } + /// Attribute name of creation method. + inline static const std::string& CREATION_METHOD_ID() + { + static const std::string MY_CREATION_METHOD_ID("interpolation_method"); + return MY_CREATION_METHOD_ID; + } + + /// Attribute name of creation method by selection + inline static const std::string& CREATION_METHOD_BY_SELECTION_ID() + { + static const std::string MY_CREATION_METHOD_BY_SELECTION_ID("by_selection"); + return MY_CREATION_METHOD_BY_SELECTION_ID; + } + + /// Attribute name of creation method analytical + inline static const std::string& CREATION_METHOD_ANALYTICAL_ID() + { + static const std::string MY_CREATION_METHOD_ANALYTICAL_ID("analytical"); + return MY_CREATION_METHOD_ANALYTICAL_ID; + } + /// Attribute name of base objects. inline static const std::string& BASE_OBJECTS_ID() { @@ -82,9 +103,79 @@ public: return MY_TANGENT_END_ID; } + /// Attribute name of x(t) equation. + inline static const std::string& XT_ID() + { + static const std::string MY_XT_ID("xt"); + return MY_XT_ID; + } + + /// Attribute name of y(t) equation. + inline static const std::string& YT_ID() + { + static const std::string MY_YT_ID("yt"); + return MY_YT_ID; + } + + /// Attribute name of z(t) equation. + inline static const std::string& ZT_ID() + { + static const std::string MY_ZT_ID("zt"); + return MY_ZT_ID; + } + + /// Attribute name of min t. + inline static const std::string& MINT_ID() + { + static const std::string MY_MINT_ID("mint"); + return MY_MINT_ID; + } + + /// Attribute name of max t. + inline static const std::string& MAXT_ID() + { + static const std::string MY_MAXT_ID("maxt"); + return MY_MAXT_ID; + } + + /// Attribute of parameter name of variable + inline static const std::string& VARIABLE_ID() + { + static const std::string MY_VARIABLE_ID("variable"); + return MY_VARIABLE_ID; + } + + /// Attribute of parameter name of value + inline static const std::string& VALUE_ID() + { + static const std::string MY_VALUE_ID("value"); + return MY_VALUE_ID; + } + + /// Attribute of parameter expression error + inline static const std::string& EXPRESSION_ERROR_ID() + { + static const std::string MY_EXPRESSION_ERROR_ID("ExpressionError"); + return MY_EXPRESSION_ERROR_ID; + } + + /// Attribute name of number of steps + inline static const std::string& NUMSTEP_ID() + { + static const std::string MY_NUMSTEP_ID("numstep"); + return MY_NUMSTEP_ID; + } + /// Default value of the closed attribute inline static bool CLOSED_DEFAULT() { return false; } + /// List of references to the arguments of this expression + inline static const std::string& ARGUMENTS_ID() + { + static const std::string MY_ARGUMENTS_ID("arguments"); + return MY_ARGUMENTS_ID; + } + /// \return the kind of a feature. BUILDPLUGIN_EXPORT virtual const std::string& getKind() { @@ -97,6 +188,21 @@ public: /// Creates a new part document if needed. BUILDPLUGIN_EXPORT virtual void execute(); + + /// Called on change of any argument-attribute of this object. + /// \param[in] theID identifier of changed attribute. + BUILDPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); + + protected: + /// Evaluates the expression x(t), y(t),z(t) in value table. + void evaluate(std::string& theError); + + /// Update coordinates x,y,z + void updateCoordinates(); + + /// Output error for python interpreter + std::string outErrorMessage; + }; #endif diff --git a/src/BuildPlugin/BuildPlugin_Plugin.cpp b/src/BuildPlugin/BuildPlugin_Plugin.cpp index e18d092cf..77a710b69 100644 --- a/src/BuildPlugin/BuildPlugin_Plugin.cpp +++ b/src/BuildPlugin/BuildPlugin_Plugin.cpp @@ -59,6 +59,8 @@ BuildPlugin_Plugin::BuildPlugin_Plugin() new BuildPlugin_ValidatorFillingSelection()); aFactory->registerValidator("BuildPlugin_ValidatorBaseForVertex", new BuildPlugin_ValidatorBaseForVertex()); + aFactory->registerValidator("BuildPlugin_ValidatorExpressionInterpolation", + new BuildPlugin_ValidatorExpressionInterpolation()); // Register this plugin. ModelAPI_Session::get()->registerPlugin(this); diff --git a/src/BuildPlugin/BuildPlugin_Validators.cpp b/src/BuildPlugin/BuildPlugin_Validators.cpp index ac454e620..f0657537d 100644 --- a/src/BuildPlugin/BuildPlugin_Validators.cpp +++ b/src/BuildPlugin/BuildPlugin_Validators.cpp @@ -20,6 +20,7 @@ #include "BuildPlugin_Validators.h" #include +#include #include #include @@ -39,6 +40,8 @@ #include #include +#include + #include #include @@ -569,3 +572,28 @@ bool BuildPlugin_ValidatorBaseForVertex::isValid(const AttributePtr& theAttribut return true; } + +//================================================================================================= +bool BuildPlugin_ValidatorExpressionInterpolation::isValid(const AttributePtr& theAttribute, + const std::list& /*theArguments*/, + Events_InfoMessage& theError) const +{ + FeaturePtr aFeature = std::dynamic_pointer_cast(theAttribute->owner()); + + AttributeStringPtr aStrAttr = + std::dynamic_pointer_cast(theAttribute); + if (!aStrAttr->isInitialized()) { + theError = "Attribute \"%1\" is not initialized."; + theError.arg(aStrAttr->id()); + return false; + } + bool isEmptyExpr = aStrAttr->value().empty(); + if (isEmptyExpr) { + theError = "Expression is empty."; + return false; + } + + theError = aFeature->string(BuildPlugin_Interpolation::EXPRESSION_ERROR_ID())->value(); + return theError.empty(); +} + diff --git a/src/BuildPlugin/BuildPlugin_Validators.h b/src/BuildPlugin/BuildPlugin_Validators.h index 9d4670ef0..e1fd0c244 100644 --- a/src/BuildPlugin/BuildPlugin_Validators.h +++ b/src/BuildPlugin/BuildPlugin_Validators.h @@ -133,4 +133,19 @@ public: Events_InfoMessage& theError) const; }; + /// \class BuildPlugin_ValidatorExpression + /// \ingroup Validators + /// \brief Validator for the expression of parameter. +class BuildPlugin_ValidatorExpressionInterpolation: public ModelAPI_AttributeValidator +{ +public: + //! Returns true if attribute has a valid parameter expression. + //! \param theAttribute the checked attribute + //! \param theArguments arguments of the attribute + //! \param theError the error string message if validation fails + virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + #endif diff --git a/src/BuildPlugin/BuildPlugin_msg_fr.ts b/src/BuildPlugin/BuildPlugin_msg_fr.ts index e6c433fbb..163ef0773 100644 --- a/src/BuildPlugin/BuildPlugin_msg_fr.ts +++ b/src/BuildPlugin/BuildPlugin_msg_fr.ts @@ -827,7 +827,41 @@ Tangentes - + + Interpolation + + Curves parameters + Paramètres de la courbe + + + + Interpolation:xt + + X(t) equation + Équation X(t) + + + + Interpolation:yt + + Y(t) equation + Équation Y(t) + + + + Interpolation:zt + + Z(t) equation + Équation Z(t) + + + + Interpolation:numstep + + Number of steps + Nombre de pas + + Polyline:base_objects diff --git a/src/BuildPlugin/CMakeLists.txt b/src/BuildPlugin/CMakeLists.txt index c018a5d7d..be036a0a6 100644 --- a/src/BuildPlugin/CMakeLists.txt +++ b/src/BuildPlugin/CMakeLists.txt @@ -23,11 +23,15 @@ INCLUDE(UnitTest) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/Events ${PROJECT_SOURCE_DIR}/src/Config ${PROJECT_SOURCE_DIR}/src/ModelAPI + ${PROJECT_SOURCE_DIR}/src/Locale ${PROJECT_SOURCE_DIR}/src/GeomAPI ${PROJECT_SOURCE_DIR}/src/GeomAlgoAPI ${PROJECT_SOURCE_DIR}/src/GeomDataAPI ${PROJECT_SOURCE_DIR}/src/GeomValidators ${PROJECT_SOURCE_DIR}/src/SketchPlugin + ${PROJECT_SOURCE_DIR}/src/InitializationPlugin + ${SUIT_INCLUDE} + ${PYTHON_INCLUDE_DIR} ) SET(PROJECT_HEADERS @@ -98,6 +102,8 @@ SET(PROJECT_LIBRARIES GeomAPI GeomAlgoAPI GeomValidators + ${PyInterp} + ${PYTHON_LIBRARIES} ) ADD_DEFINITIONS(-DBUILDPLUGIN_EXPORTS) diff --git a/src/BuildPlugin/Test/TestInterpolation.py b/src/BuildPlugin/Test/TestInterpolation.py index 2fae36742..d2460699f 100644 --- a/src/BuildPlugin/Test/TestInterpolation.py +++ b/src/BuildPlugin/Test/TestInterpolation.py @@ -172,6 +172,16 @@ model.testHaveNamingSubshapes(Interpolation_9, model, Part_3_doc) model.end() # ============================================================================= -# Test 12. Check Python dump +# Test 12. Create curve using an analytical expression +# ============================================================================= +Part_5 = model.addPart(partSet) +Part_5_doc = Part_5.document() + +Interpolation_11 = model.addInterpolation(Part_5_doc, "sin(t)","cos(t)","t", 0, 100, 10) +model.do() +model.testNbResults(Interpolation_11, 1) +model.end() +# ============================================================================= +# Test 13. Check Python dump # ============================================================================= assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING)) diff --git a/src/BuildPlugin/doc/TUI_interpolationAnalytical.rst b/src/BuildPlugin/doc/TUI_interpolationAnalytical.rst new file mode 100644 index 000000000..137be1458 --- /dev/null +++ b/src/BuildPlugin/doc/TUI_interpolationAnalytical.rst @@ -0,0 +1,12 @@ + + .. _tui_create_interpolation_analytical: + +Create Interpolation analytical +=============================== + +.. literalinclude:: examples/interpolationAnalytical.py + :linenos: + :language: python + +:download:`Download this script ` + diff --git a/src/BuildPlugin/doc/TUI_interpolationFeature.rst b/src/BuildPlugin/doc/TUI_interpolationBySelection.rst similarity index 57% rename from src/BuildPlugin/doc/TUI_interpolationFeature.rst rename to src/BuildPlugin/doc/TUI_interpolationBySelection.rst index cfcaf78bd..6e5a6a5f9 100644 --- a/src/BuildPlugin/doc/TUI_interpolationFeature.rst +++ b/src/BuildPlugin/doc/TUI_interpolationBySelection.rst @@ -1,8 +1,8 @@ - .. _tui_create_interpolation: + .. _tui_create_interpolation_by_selection: -Create Interpolation -==================== +Create Interpolation by selection +================================= .. literalinclude:: examples/interpolation.py :linenos: diff --git a/src/BuildPlugin/doc/examples/interpolationAnalytical.py b/src/BuildPlugin/doc/examples/interpolationAnalytical.py new file mode 100644 index 000000000..f39fe81ed --- /dev/null +++ b/src/BuildPlugin/doc/examples/interpolationAnalytical.py @@ -0,0 +1,9 @@ +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Interpolation_1 = model.addInterpolation(Part_1_doc, "5*sin(t)","5*cos(t)","t*0.2", 0, 25, 100) +model.do() +model.end() diff --git a/src/BuildPlugin/doc/images/CreateInterpolationAnalytical.png b/src/BuildPlugin/doc/images/CreateInterpolationAnalytical.png new file mode 100644 index 0000000000000000000000000000000000000000..2daa9459b6f25d667b1700e8df112439c7778b4a GIT binary patch literal 14456 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 literal 8000 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 diff --git a/src/BuildPlugin/doc/images/InterpolationAnalytical.png b/src/BuildPlugin/doc/images/InterpolationAnalytical.png new file mode 100644 index 0000000000000000000000000000000000000000..472c4db06af0139e7c780ad8744c33c76274ac86 GIT binary patch literal 14371 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 literal 0 HcmV?d00001 diff --git a/src/BuildPlugin/doc/images/feature_interpolation_analytical.png b/src/BuildPlugin/doc/images/feature_interpolation_analytical.png new file mode 100644 index 0000000000000000000000000000000000000000..a47790fc6a73ec27869beb46de6d26c16befa50e GIT binary patch literal 1172 zcmV;F1Z(?=P)X1^@s6z#LUx00009a7bBm000id z000id0mpBsWB>pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11R+U8 zK~zYIm6lC!(@GSEpBaxo5+!z%Ler{J1S)C822`mVU^iAr+BU zk$`-(b)3YR@m&N@6F2GQ9x00LIdk5bbKdjLh%C!83by3ajWHpF{Wkx6o5@wdvMjT| z{9S9!r%#_aK0anR94?IgjVbFWfVnxv)e0d$W41>Xd@px?CE0qdi7^0No*|TTFal91hzjml{=Zp*l3=*%^bu0Amb65a7D*!furig7x)v+U+*&cAFpwsMqU^Mk8Lo zevL7PSFc_zC4o}2l7IdBmG9rbv$nQIv)N>AZ4D_ULJ0exW!a1%<#L%I2zdMUEiYfb z#C2UpqY;%##ZrEr1bG^hB;nn=cZ|nl4h{~8qG*9=^C`A$bR37BogIAN=gyrw*6GEI z7kHjWuh&~><{$`|PN$rloDfA3Po6xnt2f5j-OQ&PKp11P`4!~Pc`fqYq-lzjlGD>u z&d<;BJdZF8+1c5_bzRalwQ0@g6af{$bUI};8m*L_d}6oTrPJxq?RKfx>uhXnFdmPY zOeO$KCKKyB7+f$M#xxoYs@3WY)EJX>yItDtHjd-01Tx0Z>2zo|n}lJ=$B!SW)oKJm zu#lBJ)$R5f@8AE!^XJdm-`__mbxn}1t*w=6V2lAV>!H)rQ);yuQ54Z=H1K_YCUfF? zo|N(rK@6Lln@Bm6U}07NPwY(O4~w9a$%GFdKG5lOsMTsbeE5)Bt+q1tX_{h;!Sg(X zkkk|Z>nO_WkdDVK_FF|AM#;`w7mN{Ld6Ua!Z| z(Gjgyi@m))&d<+D(-fGsOt07D=g<87`IDzl zpVDYFY@_Bi$g+%TwaUT4!2;FG^W zz~3I43t;_zpKss3;dvfG5U{tL zuIpOmSXT7nH)9N?Qi*eL84KAg`_x+^;apPJr zA1Jp~;8IGa(<%LaAFZ`za1=$eX1@*WZ?9yzX{{-hN<4V*V1YoM>n*5?jlb;PShRVU mG)>W3}1{rUgjp4pVqfx*x)K$3w)fLTx6$k1@&(~<^;q(iAQ0(11w1mp$r zuy82wh|fCmy_uI$k}WlQa*~Dv!$JuTW1fz$jTX`!>CQ)|bei%jG;FAhWbIIG_`xc0 zzp=|x;I&r+L(^`7ld1;;^1GaWzFV?GNTDGjp{KaJJ5evOVb?oV5o3j&LMs16=QlDi z9%&I}EoWjb=#*_st3su+z91Qe}omo#i#vKug`xTJ?I@y>t6)qDm42Oke92g52xuX;ygf%)4jfx<>SQ=Ax>I4ot0 zoRh`EKOK0LyJCj5&JG5q4pz^1+Kmz}-@k>MSWY$WZjVcjHIb2p(W$F?hQAxvX Interpolation* item or #. click |feature_interpolation.icon| **Interpolation** button in the toolbar -The following property panel will be opened: +There are two creation modes of an interpolation: + +.. figure:: images/feature_interpolation_by_selection.png + :align: left + :height: 24px + +Interpolation by selection + +.. figure:: images/feature_interpolation_analytical.png + :align: left + :height: 24px + +Interpolation analytical + +Interpolation by selection +"""""""""""""""""""""""""" +The property panel is shown below. .. figure:: images/Interpolation.png :align: center - Create an interpolation + Create an interpolation by selection Select one or several vertices or points in the viewer. @@ -59,4 +75,56 @@ The result of the operation will be a curve created from the selected shapes: Result of the operation. -**See Also** a sample TUI Script of :ref:`tui_create_interpolation` operation. +**See Also** a sample TUI Script of :ref:`tui_create_interpolation_by_selection` operation. + +Interpolation analytical +"""""""""""""""""""""""" + +The property panel is shown below. + +.. figure:: images/InterpolationAnalytical.png + :align: center + + Create an interpolation analytical + +Select one or several vertices or points in the viewer. + +- **Curves parameters** panel allows to define the mathematical expression for creating the interpolated curve. + - **X(t) equation** define the expression of X with t variable. + - **Y(t) equation** define the expression of Y with t variable. + - **Z(t) equation** define the expression of Z with t variable. + +- **Min t** define the minimun of t. + +- **Max t** define the maximum of t. + +- **Number of steps** define the number of steps. + +**Apply** button creates an interpolation. + +**Cancel** button cancels the operation. + +**TUI Commands**: + +.. py:function:: model.addInterpolation(Part_doc, xt, yt, zt, mint, maxt, nbSteps) + + :param part: The current part object. + :param string: Expression of x. + :param string: Expression of y. + :param string: Expression of z. + :param number: Minimum value of t. + :param number: Maximum value of t. + :param number: Number of steps. + :return: Result object. + +Result +"""""" + +The result of the operation will be a curve created from analytical expressions for x,y and z as functions of variable t: + +.. figure:: images/CreateInterpolationAnalytical.png + :align: center + + Result of the operation. + +**See Also** a sample TUI Script of :ref:`tui_create_interpolation_analytical` operation. diff --git a/src/BuildPlugin/icons/feature_interpolation_analytical.png b/src/BuildPlugin/icons/feature_interpolation_analytical.png new file mode 100644 index 0000000000000000000000000000000000000000..a47790fc6a73ec27869beb46de6d26c16befa50e GIT binary patch literal 1172 zcmV;F1Z(?=P)X1^@s6z#LUx00009a7bBm000id z000id0mpBsWB>pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11R+U8 zK~zYIm6lC!(@GSEpBaxo5+!z%Ler{J1S)C822`mVU^iAr+BU zk$`-(b)3YR@m&N@6F2GQ9x00LIdk5bbKdjLh%C!83by3ajWHpF{Wkx6o5@wdvMjT| z{9S9!r%#_aK0anR94?IgjVbFWfVnxv)e0d$W41>Xd@px?CE0qdi7^0No*|TTFal91hzjml{=Zp*l3=*%^bu0Amb65a7D*!furig7x)v+U+*&cAFpwsMqU^Mk8Lo zevL7PSFc_zC4o}2l7IdBmG9rbv$nQIv)N>AZ4D_ULJ0exW!a1%<#L%I2zdMUEiYfb z#C2UpqY;%##ZrEr1bG^hB;nn=cZ|nl4h{~8qG*9=^C`A$bR37BogIAN=gyrw*6GEI z7kHjWuh&~><{$`|PN$rloDfA3Po6xnt2f5j-OQ&PKp11P`4!~Pc`fqYq-lzjlGD>u z&d<;BJdZF8+1c5_bzRalwQ0@g6af{$bUI};8m*L_d}6oTrPJxq?RKfx>uhXnFdmPY zOeO$KCKKyB7+f$M#xxoYs@3WY)EJX>yItDtHjd-01Tx0Z>2zo|n}lJ=$B!SW)oKJm zu#lBJ)$R5f@8AE!^XJdm-`__mbxn}1t*w=6V2lAV>!H)rQ);yuQ54Z=H1K_YCUfF? zo|N(rK@6Lln@Bm6U}07NPwY(O4~w9a$%GFdKG5lOsMTsbeE5)Bt+q1tX_{h;!Sg(X zkkk|Z>nO_WkdDVK_FF|AM#;`w7mN{Ld6Ua!Z| z(Gjgyi@m))&d<+D(-fGsOt07D=g<87`IDzl zpVDYFY@_Bi$g+%TwaUT4!2;FG^W zz~3I43t;_zpKss3;dvfG5U{tL zuIpOmSXT7nH)9N?Qi*eL84KAg`_x+^;apPJr zA1Jp~;8IGa(<%LaAFZ`za1=$eX1@*WZ?9yzX{{-hN<4V*V1YoM>n*5?jlb;PShRVU mG)>W3}1{rUgjp4pVqfx*x)K$3w)fLTx6$k1@&(~<^;q(iAQ0(11w1mp$r zuy82wh|fCmy_uI$k}WlQa*~Dv!$JuTW1fz$jTX`!>CQ)|bei%jG;FAhWbIIG_`xc0 zzp=|x;I&r+L(^`7ld1;;^1GaWzFV?GNTDGjp{KaJJ5evOVb?oV5o3j&LMs16=QlDi z9%&I}EoWjb=#*_st3su+z91Qe}omo#i#vKug`xTJ?I@y>t6)qDm42Oke92g52xuX;ygf%)4jfx<>SQ=Ax>I4ot0 zoRh`EKOK0LyJCj5&JG5q4pz^1+Kmz}-@k>MSWY$WZjVcjHIb2p(W$F?hQAxvX - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/GeomValidators/CMakeLists.txt b/src/GeomValidators/CMakeLists.txt index 40890c4ca..c2ff50c8d 100644 --- a/src/GeomValidators/CMakeLists.txt +++ b/src/GeomValidators/CMakeLists.txt @@ -78,6 +78,7 @@ INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/src/Events ${PROJECT_SOURCE_DIR}/src/GeomAPI ${PROJECT_SOURCE_DIR}/src/GeomDataAPI + ${PROJECT_SOURCE_DIR}/src/BuildPlugin ) INSTALL(TARGETS GeomValidators DESTINATION ${SHAPER_INSTALL_PLUGIN_FILES}) diff --git a/src/GeomValidators/GeomValidators_MinObjectsSelected.cpp b/src/GeomValidators/GeomValidators_MinObjectsSelected.cpp index 174d1b71c..93968cf48 100644 --- a/src/GeomValidators/GeomValidators_MinObjectsSelected.cpp +++ b/src/GeomValidators/GeomValidators_MinObjectsSelected.cpp @@ -21,7 +21,10 @@ #include +#include + #include +#include #include //================================================================================================= @@ -29,17 +32,25 @@ bool GeomValidators_MinObjectsSelected::isValid(const std::shared_ptr& theArguments, Events_InfoMessage& theError) const { - if(theArguments.size() != 2) { + if (theArguments.size() != 2) { // LCOV_EXCL_START theError = "Error: Wrong number of arguments (expected 2): selection list id and min number of objects"; return false; // LCOV_EXCL_STOP } + //"Interpolation" + if (theFeature->name().substr(0, 6) == L"Interp") + { + AttributeStringPtr anAttr =theFeature->string(BuildPlugin_Interpolation::CREATION_METHOD_ID()); + if (anAttr->isInitialized()) + if (anAttr->value() == BuildPlugin_Interpolation::CREATION_METHOD_ANALYTICAL_ID()) + return true; + } std::string aSelectionListId = theArguments.front(); AttributeSelectionListPtr anAttrSelList = theFeature->selectionList(aSelectionListId); - if(!anAttrSelList.get()) { + if (!anAttrSelList.get()) { // LCOV_EXCL_START theError = "Error: Could not get attribute \"%1\"."; theError.arg(aSelectionListId); @@ -50,7 +61,7 @@ bool GeomValidators_MinObjectsSelected::isValid(const std::shared_ptr #include -#include #include +#include + +#include + #include #include @@ -31,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -44,8 +48,10 @@ #include #include #include +#include +#include -//------------------------------------------------------------------------------ +//================================================================================================= // Tools std::wstring toString(double theValue) @@ -63,24 +69,26 @@ std::set toSet(const std::list& theContainer) return std::set(theContainer.begin(), theContainer.end()); } -//------------------------------------------------------------------------------ - +//================================================================================================= InitializationPlugin_EvalListener::InitializationPlugin_EvalListener() { Events_Loop* aLoop = Events_Loop::loop(); aLoop->registerListener(this, ModelAPI_AttributeEvalMessage::eventId(), NULL, true); aLoop->registerListener(this, ModelAPI_ParameterEvalMessage::eventId(), NULL, true); + aLoop->registerListener(this, ModelAPI_BuildEvalMessage::eventId(), NULL, true); aLoop->registerListener(this, ModelAPI_ComputePositionsMessage::eventId(), NULL, true); myInterp = std::shared_ptr(new InitializationPlugin_PyInterp()); myInterp->initialize(); } +//================================================================================================= InitializationPlugin_EvalListener::~InitializationPlugin_EvalListener() { } +//================================================================================================= void InitializationPlugin_EvalListener::processEvent( const std::shared_ptr& theMessage) { @@ -105,9 +113,116 @@ void InitializationPlugin_EvalListener::processEvent( std::shared_ptr aMsg = std::dynamic_pointer_cast(theMessage); aMsg->setPositions(myInterp->positions(aMsg->expression(), aMsg->parameter())); + } 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; + bool anIsFirstTime = true; + anExprAttr = aParam->string(BuildPlugin_Interpolation::XT_ID()); + std::wstring anExpX = anExprAttr->isUValue() ? + Locale::Convert::toWString(anExprAttr->valueU()) : + Locale::Convert::toWString(anExprAttr->value()); + anExpX.erase(std::remove(anExpX.begin(), anExpX.end(), ' '), anExpX.end()); + anExprAttr = aParam->string(BuildPlugin_Interpolation::YT_ID()); + std::wstring anExpY = anExprAttr->isUValue() ? + Locale::Convert::toWString(anExprAttr->valueU()) : + Locale::Convert::toWString(anExprAttr->value()); + anExpY.erase(std::remove(anExpY.begin(), anExpY.end(), ' '), anExpY.end()); + anExprAttr = aParam->string(BuildPlugin_Interpolation::ZT_ID()); + std::wstring anExpZ = anExprAttr->isUValue() ? + Locale::Convert::toWString(anExprAttr->valueU()) : + Locale::Convert::toWString(anExprAttr->value()); + anExpZ.erase(std::remove(anExpZ.begin(),anExpZ.end(), ' '), anExpZ.end()); + + for (int step =0; step < anValueAttr->rows(); step++) { + aVal.myDouble = evaluate(anVar, + anValueAttr->value(step,0).myDouble, + aParam, + anExpX, + anError, + aParamsList, + anIsFirstTime); + if (!anError.empty()) break; + anValueAttr->setValue(aVal,step,1); + aVal.myDouble = evaluate(anVar, + anValueAttr->value(step,0).myDouble, + aParam, + anExpY, + anError, + aParamsList, + anIsFirstTime); + if (!anError.empty()) break; + anValueAttr->setValue(aVal,step,2); + aVal.myDouble = evaluate(anVar, + anValueAttr->value(step,0).myDouble, + aParam, + anExpZ, + anError, + aParamsList, + anIsFirstTime); + if (!anError.empty()) break; + anValueAttr->setValue(aVal,step,3); + if (anIsFirstTime) + anIsFirstTime = false; + } + aMsg->setResults(aParamsList, anError); + } +} + +//================================================================================================= +double InitializationPlugin_EvalListener::evaluate(std::wstring& theVariable, + double theValueVariable, + FeaturePtr theParameter, + const std::wstring& theExpression, + std::string& theError, + std::list >& theParamsList, + bool theIsFirstTime) +{ + std::list aContext; + aContext.push_back(theVariable + L"=" + toString(theValueVariable)); + myInterp->extendLocalContext(aContext); + aContext.clear(); + + std::list anExprParams = myInterp->compile(theExpression); + // find expression's params in the model + 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. + if (!ModelAPI_Tools::findVariable(FeaturePtr(), + *it, aValue, aParamRes, theParameter->document())) + continue; + + if (theIsFirstTime) + { + std::list::iterator anIter = + std::find(theParamsList.begin(),theParamsList.end(), aParamRes); + if (anIter == theParamsList.end()) + theParamsList.push_back(aParamRes); + } + + aContext.push_back(*it + L"=" + toString(aValue)); } + myInterp->extendLocalContext(aContext); + double result = myInterp->evaluate(theExpression, theError); + myInterp->clearLocalContext(); + return result; } +//================================================================================================= double InitializationPlugin_EvalListener::evaluate(FeaturePtr theParameter, const std::wstring& theExpression, std::string& theError, std::list >& theParamsList, @@ -117,7 +232,7 @@ double InitializationPlugin_EvalListener::evaluate(FeaturePtr theParameter, // find expression's params in the model std::list aContext; std::list::iterator it = anExprParams.begin(); - for ( ; it != anExprParams.end(); it++) { + for (; it != anExprParams.end(); it++) { double aValue; ResultParameterPtr aParamRes; // If variable does not exist python interpreter will generate an error. It is OK. @@ -137,6 +252,7 @@ double InitializationPlugin_EvalListener::evaluate(FeaturePtr theParameter, return result; } +//================================================================================================= void InitializationPlugin_EvalListener::processEvaluationEvent( const std::shared_ptr& theMessage) { @@ -158,8 +274,7 @@ void InitializationPlugin_EvalListener::processEvaluationEvent( toSet(myInterp->compile(anAttribute->text())) : std::set()); anAttribute->setExpressionInvalid(!isValid); anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError); - } else - if (aMessage->attribute()->attributeType() == ModelAPI_AttributeDouble::typeId()) { + } else if (aMessage->attribute()->attributeType() == ModelAPI_AttributeDouble::typeId()) { AttributeDoublePtr anAttribute = std::dynamic_pointer_cast(aMessage->attribute()); std::string anError; @@ -171,8 +286,7 @@ void InitializationPlugin_EvalListener::processEvaluationEvent( toSet(myInterp->compile(anAttribute->text())) : std::set()); anAttribute->setExpressionInvalid(!isValid); anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError); - } else - if (aMessage->attribute()->attributeType() == GeomDataAPI_Point::typeId()) { + } else if (aMessage->attribute()->attributeType() == GeomDataAPI_Point::typeId()) { AttributePointPtr anAttribute = std::dynamic_pointer_cast(aMessage->attribute()); std::wstring aText[] = { @@ -198,8 +312,7 @@ void InitializationPlugin_EvalListener::processEvaluationEvent( anAttribute->setCalculatedValue(aCalculatedValue[0], aCalculatedValue[1], aCalculatedValue[2]); - } else - if (aMessage->attribute()->attributeType() == GeomDataAPI_Point2D::typeId()) { + } else if (aMessage->attribute()->attributeType() == GeomDataAPI_Point2D::typeId()) { AttributePoint2DPtr anAttribute = std::dynamic_pointer_cast(aMessage->attribute()); std::wstring aText[] = { @@ -225,6 +338,7 @@ void InitializationPlugin_EvalListener::processEvaluationEvent( } } +//================================================================================================= void InitializationPlugin_EvalListener::initDataModel() { myInterp->runString("import salome_iapp;salome_iapp.register_module_in_study(\"Shaper\")"); diff --git a/src/InitializationPlugin/InitializationPlugin_EvalListener.h b/src/InitializationPlugin/InitializationPlugin_EvalListener.h index 756b335ec..5036863a9 100644 --- a/src/InitializationPlugin/InitializationPlugin_EvalListener.h +++ b/src/InitializationPlugin/InitializationPlugin_EvalListener.h @@ -50,11 +50,21 @@ class InitializationPlugin_EvalListener : public Events_Listener protected: /// Evaluates theExpression and returns its value. - double evaluate(std::shared_ptr theParameter, + double evaluate(std::shared_ptr theParameter, const std::wstring& theExpression, std::string& theError, std::list >& theParamsList, const bool theIsParameter = false); + /// Evaluates theExpression with variable local 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, + bool theIsFirstTime); + + /// Processes Evaluation event. void processEvaluationEvent(const std::shared_ptr& theMessage); diff --git a/src/ModelAPI/ModelAPI_Events.cpp b/src/ModelAPI/ModelAPI_Events.cpp index cebf9ca79..054d0bacf 100644 --- a/src/ModelAPI/ModelAPI_Events.cpp +++ b/src/ModelAPI/ModelAPI_Events.cpp @@ -188,6 +188,49 @@ const std::string& ModelAPI_ParameterEvalMessage::error() const return myError; } +ModelAPI_BuildEvalMessage::ModelAPI_BuildEvalMessage( + const Events_ID theID, const void* theSender) + : Events_Message(theID, theSender), myIsProcessed(false) +{} + +ModelAPI_BuildEvalMessage::~ModelAPI_BuildEvalMessage() +{} + +FeaturePtr ModelAPI_BuildEvalMessage::parameter() const +{ + return myParam; +} + +void ModelAPI_BuildEvalMessage::setParameter(FeaturePtr theParam) +{ + myParam = theParam; +} + +void ModelAPI_BuildEvalMessage::setResults( + const std::list >& theParamsList, + const std::string& theError) +{ + myParamsList = theParamsList; + myError = theError; + myIsProcessed = true; +} + +const std::list >& + ModelAPI_BuildEvalMessage::params() const +{ + return myParamsList; +} + +bool ModelAPI_BuildEvalMessage::isProcessed() +{ + return myIsProcessed; +} + +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..e11ab4b06 100644 --- a/src/ModelAPI/ModelAPI_Events.h +++ b/src/ModelAPI/ModelAPI_Events.h @@ -352,6 +352,57 @@ 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 + std::string myError; ///< error of processing, empty if there is no error + /// result of processing, list of parameters in expression found + std::list > myParamsList; + + public: + /// Static. Returns EventID of the message. + MODELAPI_EXPORT static Events_ID& eventId() + { + static const char * MY_BUILD_EVALUATION_EVENT_ID("BuildEvaluationRequest"); + 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 std::string& theError); + /// Returns the results of processing: list of parameters found in the expression + MODELAPI_EXPORT const std::list >& params() const; + /// Returns true if the expression is processed + MODELAPI_EXPORT bool isProcessed(); + + /// 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 -- 2.39.2