diff --git a/doc/gui/images/nbsegment_beta_law_formula.png b/doc/gui/images/nbsegment_beta_law_formula.png new file mode 100755 index 000000000..fb61edeab Binary files /dev/null and b/doc/gui/images/nbsegment_beta_law_formula.png differ diff --git a/doc/gui/images/nbsegments_beta_law_dlg.png b/doc/gui/images/nbsegments_beta_law_dlg.png new file mode 100755 index 000000000..dbed86935 Binary files /dev/null and b/doc/gui/images/nbsegments_beta_law_dlg.png differ diff --git a/doc/gui/images/nbsegments_beta_law_example.png b/doc/gui/images/nbsegments_beta_law_example.png new file mode 100755 index 000000000..10b108248 Binary files /dev/null and b/doc/gui/images/nbsegments_beta_law_example.png differ diff --git a/doc/gui/input/1d_meshing_hypo.rst b/doc/gui/input/1d_meshing_hypo.rst index 4fc4c2d4e..eba4a83b6 100644 --- a/doc/gui/input/1d_meshing_hypo.rst +++ b/doc/gui/input/1d_meshing_hypo.rst @@ -199,6 +199,23 @@ The node distribution is computed so that to have the density function integral .. image:: ../images/distributionwithtabledensity.png :align: center +**Beta Law Distribution** - is given by the following formula (see image below) where **t** is the position of the point in the segment [0, 1]. + +.. image:: ../images/nbsegment_beta_law_formula.png + :align: center + +The beta parameter is usually set between 1.01 (narrow mesh) and 1.00001 (very narrow mesh). +Values between [-1, 1] are forbidden to ensure validity of the log. +Negative values are allowed and result with positions distributed in the opposite direction. + +.. image:: ../images/nbsegments_beta_law_dlg.png + :align: center + +Below is an example of **Beta Law Distribution** for a face that was done using default **Expansion coefficient** value 1.01. + +.. image:: ../images/nbsegments_beta_law_example.png + :align: center + **See Also** a sample TUI Script of :ref:`Defining Number of Segments ` hypothesis operation. diff --git a/idl/SMESH_BasicHypothesis.idl b/idl/SMESH_BasicHypothesis.idl index eb46c3c78..c8fcd58e0 100644 --- a/idl/SMESH_BasicHypothesis.idl +++ b/idl/SMESH_BasicHypothesis.idl @@ -201,6 +201,18 @@ module StdMeshers double GetScaleFactor() raises (SALOME::SALOME_Exception); + /*! + * Sets coefficient for Beta Law distribution + */ + void SetBeta(in double beta) + raises (SALOME::SALOME_Exception); + + /*! + * Returns coefficient for Beta Law distribution + */ + double GetBeta() + raises (SALOME::SALOME_Exception); + /*! * Sets parameter value for distribution DT_TabFunc */ diff --git a/src/SMESHGUI/CMakeLists.txt b/src/SMESHGUI/CMakeLists.txt index 6d83cf954..a3cdc6197 100644 --- a/src/SMESHGUI/CMakeLists.txt +++ b/src/SMESHGUI/CMakeLists.txt @@ -89,6 +89,7 @@ SET(_moc_HEADERS SMESHGUI_CreatePatternDlg.h SMESHGUI_NodesDlg.h SMESHGUI_SpinBox.h + SMESHGUI_SpinBoxForbiddendRange.h SMESHGUI_TransparencyDlg.h SMESHGUI_ClippingDlg.h SMESHGUI_GroupDlg.h @@ -203,6 +204,7 @@ SET(_other_SOURCES SMESHGUI_CreatePatternDlg.cxx SMESHGUI_NodesDlg.cxx SMESHGUI_SpinBox.cxx + SMESHGUI_SpinBoxForbiddendRange.cxx SMESHGUI_TransparencyDlg.cxx SMESHGUI_ClippingDlg.cxx SMESHGUI_GroupDlg.cxx diff --git a/src/SMESHGUI/SMESHGUI_SpinBoxForbiddendRange.cxx b/src/SMESHGUI/SMESHGUI_SpinBoxForbiddendRange.cxx new file mode 100644 index 000000000..bc0a54c2d --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_SpinBoxForbiddendRange.cxx @@ -0,0 +1,197 @@ +// Copyright (C) 2007-2024 CEA, EDF, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_SpinBoxForbiddendRange.cxx +// Author : Konstantin Leontev, Open CASCADE S.A.S. +// SMESH includes +// +#include "SMESHGUI_SpinBoxForbiddendRange.h" + +#include + +#include "utilities.h" + + +//================================================================================= +// class : SMESHGUI_SpinBoxForbiddendRange() +// purpose : constructor +//================================================================================= +SMESHGUI_SpinBoxForbiddendRange::SMESHGUI_SpinBoxForbiddendRange(QWidget* parent) + : SMESHGUI_SpinBox(parent), forbiddenMin(0.0), forbiddenMax(1.0) +{ + // This property allows to return QValidator::Invalid from QtxDoubleSpinBox::validate() + // for any values outside of the min/max range. + // Otherwise a user could type any intermediate value. + setProperty("strict_validity_check", true); +} + +//================================================================================= +// function : ~SMESHGUI_SpinBoxForbiddendRange() +// purpose : destructor +//================================================================================= +SMESHGUI_SpinBoxForbiddendRange::~SMESHGUI_SpinBoxForbiddendRange() +{ +} + +//================================================================================= +// function : stepBy() +// purpose : Validates value in ranges on each step +//================================================================================= +void SMESHGUI_SpinBoxForbiddendRange::stepBy(const int steps) +{ + double const newValue = value() + (steps * singleStep()); + double const validatedValue = GetValueInRange(newValue); + + setValue(validatedValue); +} + +//================================================================================= +// function : valueFromText() +// purpose : Validates value entered by user +//================================================================================= +double SMESHGUI_SpinBoxForbiddendRange::valueFromText(QString const& text) const +{ + double const newValue = SMESHGUI_SpinBox::valueFromText(text); + return GetValueInRange(newValue); +} + +QValidator::State SMESHGUI_SpinBoxForbiddendRange::validate(QString& text, int& pos) const +{ + // Do parent's min/max and all other validation first + const QValidator::State res = SMESHGUI_SpinBox::validate(text, pos); + if (res != QValidator::Acceptable) + return res; + + // Now we know that we have a valid numeric value in the text + // that we can check against forbidden range. + bool ok = false; + const double inputValue = text.toDouble(&ok); + + // Return intermediate to allow a user input any value. + // If press ok button, it will be checked again with checkRange() call. + const QValidator::State state = ok && IsValueInRange(inputValue) ? + QValidator::Acceptable : QValidator::Intermediate; + + showValidationToolTip(state); + + return state; +} + +//================================================================================= +// function : SetForbiddenRange() +// purpose : Sets min and max values for forbidden range +//================================================================================= +void SMESHGUI_SpinBoxForbiddendRange::SetForbiddenRange(const double min, const double max) +{ + forbiddenMin = min; + forbiddenMax = max; +} + +//================================================================================= +// function : checkRange() +// purpose : adds checking against forbidden range to the parent's inmplementation +//================================================================================= +bool SMESHGUI_SpinBoxForbiddendRange::checkRange(const double value) const +{ + return SalomeApp_DoubleSpinBox::checkRange(value) && IsValueInRange(value); +} + +//================================================================================= +// function : IsValueInRange() +// purpose : Validates a given value against forbidden range +//================================================================================= +bool SMESHGUI_SpinBoxForbiddendRange::IsValueInRange(const double inputValue) const +{ + // A given value must be outside of [forbiddenMin, forbiddenMax] scope + return inputValue < forbiddenMin || inputValue > forbiddenMax; +} + +//================================================================================= +// function : GetValueInRange() +// purpose : Validates value considering forbidden range +//================================================================================= +double SMESHGUI_SpinBoxForbiddendRange::GetValueInRange(const double inputValue) const +{ + // Check if a given value is outside of [forbiddenMin, forbiddenMax] scope + if (IsValueInRange(inputValue)) + return inputValue; + + const double curValue = value(); + const double step = SMESHGUI_SpinBox::singleStep(); + + // Find the nearest value outside of forbidden range + if (inputValue > curValue) + return forbiddenMax + step; + else + return forbiddenMin - step; +} + +//================================================================================= +// function : showValidationToolTip() +// purpose : Shows tooltip with a valid range +//================================================================================= +void SMESHGUI_SpinBoxForbiddendRange::showValidationToolTip(const QValidator::State state) const +{ + if (state == QValidator::Acceptable) + { + QToolTip::hideText(); + return; + } + + // All the rest is an edited copy from SalomeApp_DoubleSpinBox::validate() + + // Get position and min/max values + const QPoint pos(size().width(), 0.); + const QPoint globalPos = mapToGlobal(pos); + const QString minVal = textFromValue(minimum()); + const QString maxVal = textFromValue(maximum()); + + // Same stuff as in QtxDoubleSpinBox::textFromValue() + int digits = getPrecision(); + + // For 'g' format, max. number of digits after the decimal point is getPrecision() - 1 + // See also QtxDoubleSpinBox::validate() + if (digits < 0) + digits = qAbs(digits) - 1; + + QString msg = QString(tr("VALID_RANGE_NOVAR_MSG")). + arg(minVal). + arg(forbiddenMin). + arg(forbiddenMax). + arg(maxVal). + arg(digits); + + // It is not possible to hide and shortly afterwards reshow a tooltip + // with the same text because of some interference between the internal hideTimer of the tooltip and + // the QToolTip::showText(). As a result, the tooltip does not persist - it disappears immediately. + // If we are here, then QToolTip::hideText() was just called from the parent. + // So, below is a workaround that changes the text and lets the tooltip stay in place. + toolTipFlag = !toolTipFlag; + if (toolTipFlag) + { + msg.append(" "); + } + + // Show tooltip + QToolTip::showText(globalPos, msg, const_cast(this)); +} diff --git a/src/SMESHGUI/SMESHGUI_SpinBoxForbiddendRange.h b/src/SMESHGUI/SMESHGUI_SpinBoxForbiddendRange.h new file mode 100644 index 000000000..97bf54df1 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_SpinBoxForbiddendRange.h @@ -0,0 +1,66 @@ +// Copyright (C) 2007-2024 CEA, EDF, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_SpinBoxForbiddendRange.h +// Author : Konstantin Leontev, Open CASCADE S.A.S. +// +#ifndef SMESHGUI_SPINBOXFORBIDDENRANGE_H +#define SMESHGUI_SPINBOXFORBIDDENRANGE_H + +// SMESH includes +#include "SMESHGUI_SpinBox.h" + + +//================================================================================= +// class : SMESHGUI_SpinBoxForbiddendRange +// purpose : Allows validating with a range of forbidden values +//================================================================================= +class SMESHGUI_EXPORT SMESHGUI_SpinBoxForbiddendRange : public SMESHGUI_SpinBox +{ + Q_OBJECT + +public: + SMESHGUI_SpinBoxForbiddendRange(QWidget*); + ~SMESHGUI_SpinBoxForbiddendRange(); + + virtual void stepBy(const int steps) override; + virtual double valueFromText(QString const& text) const override; + virtual QValidator::State validate(QString& text, int& pos) const override; + + void SetForbiddenRange(const double min, const double max); + +protected: + virtual bool checkRange(const double value) const override; + bool IsValueInRange(const double value) const; + double GetValueInRange(const double value) const; + + virtual void showValidationToolTip(const QValidator::State state) const override; +private: + double forbiddenMin; + double forbiddenMax; + + mutable bool toolTipFlag = false; +}; + +#endif // SMESHGUI_SPINBOXFORBIDDENRANGE_H + diff --git a/src/SMESHGUI/SMESH_msg_en.ts b/src/SMESHGUI/SMESH_msg_en.ts index 5e07a0d31..7d40dd113 100644 --- a/src/SMESHGUI/SMESH_msg_en.ts +++ b/src/SMESHGUI/SMESH_msg_en.ts @@ -9154,4 +9154,12 @@ red in the Object Browser. Mean Size + + SMESHGUI_SpinBoxForbiddendRange + + VALID_RANGE_NOVAR_MSG + Specify a floating-point value in range [%1; %2) U (%3; %4] +with %5-digit precision + + diff --git a/src/SMESHGUI/SMESH_msg_fr.ts b/src/SMESHGUI/SMESH_msg_fr.ts index c6c37fef5..e528d3e8b 100644 --- a/src/SMESHGUI/SMESH_msg_fr.ts +++ b/src/SMESHGUI/SMESH_msg_fr.ts @@ -9172,4 +9172,12 @@ en rouge dans le browser. Taille moyenne + + SMESHGUI_SpinBoxForbiddendRange + + VALID_RANGE_NOVAR_MSG + Spécifier une valeur à virgule flottante dans la plage [%1; %2) U (%3; %4] +avec une précision de %5 chiffres + + diff --git a/src/SMESHGUI/SMESH_msg_ja.ts b/src/SMESHGUI/SMESH_msg_ja.ts index 37cc0a015..1cf301ad4 100644 --- a/src/SMESHGUI/SMESH_msg_ja.ts +++ b/src/SMESHGUI/SMESH_msg_ja.ts @@ -8286,4 +8286,12 @@ pip install meshio[all] オプション値 + + SMESHGUI_SpinBoxForbiddendRange + + VALID_RANGE_NOVAR_MSG + 範囲内の浮動小数点値を指定します [%1; %2) U (%3; %4] +%5 桁の精度 + + diff --git a/src/SMESH_SWIG/StdMeshersBuilder.py b/src/SMESH_SWIG/StdMeshersBuilder.py index 280ae889f..1b76873ed 100644 --- a/src/SMESH_SWIG/StdMeshersBuilder.py +++ b/src/SMESH_SWIG/StdMeshersBuilder.py @@ -188,7 +188,7 @@ class StdMeshersBuilder_Segment(Mesh_Algorithm): hyp.SetUsePreestimatedLength( length == 0.0 ) return hyp - def NumberOfSegments(self, n, s=[], reversedEdges=[], UseExisting=0): + def NumberOfSegments(self, n, s=[], reversedEdges=[], UseExisting=0, beta=None): """ Defines "NumberOfSegments" hypothesis to cut an edge in a fixed number of segments @@ -199,6 +199,7 @@ class StdMeshersBuilder_Segment(Mesh_Algorithm): A list item can also be a tuple (edge, 1st_vertex_of_edge) UseExisting: if ==true - searches for an existing hypothesis created with the same parameters, else (default) - create a new one + beta: beta coefficient for Beta Law distribution Returns: an instance of StdMeshers_NumberOfSegments hypothesis @@ -209,15 +210,22 @@ class StdMeshersBuilder_Segment(Mesh_Algorithm): reversedEdges, UseExisting = [], reversedEdges entry = self.MainShapeEntry() reversedEdgeInd = self.ReversedEdgeIndices(reversedEdges) - if not s: - hyp = self.Hypothesis("NumberOfSegments", [n, reversedEdgeInd, entry], - UseExisting=UseExisting, - CompareMethod=self._compareNumberOfSegments) - else: - hyp = self.Hypothesis("NumberOfSegments", [n,s, reversedEdgeInd, entry], - UseExisting=UseExisting, - CompareMethod=self._compareNumberOfSegments) + + args = [n, reversedEdgeInd, entry] + if s: + args.insert(1, s) + elif beta: + args.append(beta) + + hyp = self.Hypothesis( + "NumberOfSegments", args, + UseExisting=UseExisting, CompareMethod=self._compareNumberOfSegments) + + if s: hyp.SetScaleFactor(s) + elif beta: + hyp.SetBeta(s) + hyp.SetNumberOfSegments(n) hyp.SetReversedEdges( reversedEdgeInd ) hyp.SetObjectEntry( entry ) diff --git a/src/StdMeshers/StdMeshers_NumberOfSegments.cxx b/src/StdMeshers/StdMeshers_NumberOfSegments.cxx index c2a89b38c..338f3ca52 100644 --- a/src/StdMeshers/StdMeshers_NumberOfSegments.cxx +++ b/src/StdMeshers/StdMeshers_NumberOfSegments.cxx @@ -139,7 +139,7 @@ smIdType StdMeshers_NumberOfSegments::GetNumberOfSegments() const void StdMeshers_NumberOfSegments::SetDistrType(DistrType typ) { - if (typ < DT_Regular || typ > DT_ExprFunc) + if (!IsValidDistrType(typ)) throw SALOME_Exception(LOCALIZED("distribution type is out of range")); if (typ != _distrType) @@ -166,6 +166,18 @@ StdMeshers_NumberOfSegments::DistrType StdMeshers_NumberOfSegments::GetDistrType */ //================================================================================ +bool StdMeshers_NumberOfSegments::IsValidDistrType(int distrType) const +{ + // DistrType is sequential, so we can just check against its first and last values + return distrType >= DT_Regular && distrType <= DT_BetaLaw; +} + +//================================================================================ +/*! + * + */ +//================================================================================ + void StdMeshers_NumberOfSegments::SetScaleFactor(double scaleFactor) { if (scaleFactor < PRECISION) @@ -198,6 +210,43 @@ double StdMeshers_NumberOfSegments::GetScaleFactor() const return _scaleFactor; } +//================================================================================ +/*! + * + */ +//================================================================================ + +void StdMeshers_NumberOfSegments::SetBeta(double beta) +{ + if (_distrType != DT_BetaLaw) + throw SALOME_Exception(LOCALIZED("not a Beta Law distribution")); + + const double diff = fabs(fabs(_beta) - fabs(beta)); + if (diff <= PRECISION) + { + // Check for a special case where we have values with + // equal base but opposite signs like -1.01 and 1.01 + if (std::signbit(_beta) == std::signbit(beta)) + return; + } + + _beta = beta; + NotifySubMeshesHypothesisModification(); +} + +//================================================================================ +/*! + * + */ +//================================================================================ + +double StdMeshers_NumberOfSegments::GetBeta() const +{ + if (_distrType != DT_BetaLaw) + throw SALOME_Exception(LOCALIZED("not a Beta Law distribution")); + return _beta; +} + //================================================================================ /*! * @@ -492,6 +541,9 @@ ostream & StdMeshers_NumberOfSegments::SaveTo(ostream & save) case DT_ExprFunc: save << " " << _func; break; + case DT_BetaLaw: + save << " " << _beta; + break; case DT_Regular: default: break; @@ -542,7 +594,7 @@ istream & StdMeshers_NumberOfSegments::LoadFrom(istream & load) // supposing that this hypothesis was written in the new format if (isOK) { - if (a < DT_Regular || a > DT_ExprFunc) + if (!IsValidDistrType(a)) _distrType = DT_Regular; else _distrType = (DistrType) a; @@ -607,6 +659,22 @@ istream & StdMeshers_NumberOfSegments::LoadFrom(istream & load) } } break; + + case DT_BetaLaw: + { + isOK = static_cast(load >> b); + if (isOK) + _beta = b; + else + { + load.clear(ios::badbit | load.rdstate()); + // this can mean, that the hypothesis is stored in old format + _distrType = DT_Regular; + _scaleFactor = scale_factor; + } + } + break; + case DT_Regular: default: break; diff --git a/src/StdMeshers/StdMeshers_NumberOfSegments.hxx b/src/StdMeshers/StdMeshers_NumberOfSegments.hxx index 4e637ba44..38a85fca8 100644 --- a/src/StdMeshers/StdMeshers_NumberOfSegments.hxx +++ b/src/StdMeshers/StdMeshers_NumberOfSegments.hxx @@ -72,7 +72,8 @@ public: DT_Regular, //!< equidistant distribution DT_Scale, //!< scale distribution DT_TabFunc, //!< distribution with density function presented by table - DT_ExprFunc //!< distribution with density function presented by expression + DT_ExprFunc, //!< distribution with density function presented by expression + DT_BetaLaw //!< distribution with Beta Law using parameter beta (expansion coefficient) }; /*! @@ -85,6 +86,11 @@ public: */ DistrType GetDistrType() const; + /*! + * \brief Check if a given int falls into distribution type scope + */ + bool IsValidDistrType(int distrType) const; + /*! * \brief Set scale factor for scale distribution * \param scaleFactor - positive value different from 1 @@ -101,6 +107,21 @@ public: */ double GetScaleFactor() const; + /*! + * \brief Set beta coefficient for Beta Law distribution + * \param beta - usually set between 1.01 (narrow mesh) and 1.00001 (very narrow mesh) + * + * Throws SALOME_Exception if distribution type is not DT_BetaLaw + */ + virtual void SetBeta(double scaleFactor); + + /*! + * \brief Get beta coefficient for Beta Law distribution + * + * Throws SALOME_Exception if distribution type is not DT_BetaLaw + */ + double GetBeta() const; + /*! * \brief Set table function for distribution DT_TabFunc * \param table - this vector contains the pairs (parameter, value) @@ -184,6 +205,7 @@ protected: smIdType _numberOfSegments; //!< an edge will be split on to this number of segments DistrType _distrType; //!< the type of distribution of density function double _scaleFactor; //!< the scale parameter for DT_Scale + double _beta; //!< beta coefficient for DT_BetaLaw std::vector _table, _distr; //!< the table for DT_TabFunc, a sequence of pairs of numbers std::string _func; //!< the expression of the function for DT_ExprFunc int _convMode; //!< flag of conversion mode: 0=exponent, 1=cut negative diff --git a/src/StdMeshers/StdMeshers_Regular_1D.cxx b/src/StdMeshers/StdMeshers_Regular_1D.cxx index 44c7c1a75..13b63bcc6 100644 --- a/src/StdMeshers/StdMeshers_Regular_1D.cxx +++ b/src/StdMeshers/StdMeshers_Regular_1D.cxx @@ -209,6 +209,10 @@ bool StdMeshers_Regular_1D::CheckHypothesis( SMESH_Mesh& aMesh, _svalue[ EXPR_FUNC_IND ] = hyp->GetExpressionFunction(); _revEdgesIDs = hyp->GetReversedEdges(); break; + case StdMeshers_NumberOfSegments::DT_BetaLaw: + _value[BETA_IND] = hyp->GetBeta(); + _revEdgesIDs = hyp->GetReversedEdges(); + break; case StdMeshers_NumberOfSegments::DT_Regular: break; default: @@ -714,6 +718,74 @@ void StdMeshers_Regular_1D::redistributeNearVertices (SMESH_Mesh & theM } } +bool StdMeshers_Regular_1D::computeBetaLaw( + Adaptor3d_Curve& theC3d, + std::list& theParams, + double f, + double theLength, + double beta, + int nbSegments, + bool theReverse + ) +{ + // Implemented with formula, where h is the position of a point on the segment [0,1]: + // ratio=(1+beta)/(beta -1) + // zlog=log(ratio) + // puiss=exp(zlog*(1-h)) + // rapp=(1-puiss)/(1+puiss) + // f(h) =1+beta*rapp + // + // Look at https://gitlab.onelab.info/gmsh/gmsh/-/commit/d581b381f2b8639fba40f2e771e2573d1a0f8424 + // Especially gmsh/src/mesh/meshGEdge.cpp, 507: createPoints() + + if (theReverse) + { + beta *= -1; + } + + MESSAGE("Compute BetaLaw. beta: " << beta); + + // Prepare a temp storage for position values + const int nbNewPoints = nbSegments - 1; + std::vector t(nbNewPoints); + + // Calculate position values with beta for each point + const double zlog = log((1. + beta) / (beta - 1.)); + for(smIdType i = 0; i < nbNewPoints; i++) + { + const double eta = (double)(i + 1) / nbSegments; + const double power = exp(zlog * (1. - eta)); + const double ratio = (1. - power) / (1. + power); + const double pos = 1.0 + beta * ratio; + + // Check if we need to reverse distribution + if (beta > 0) + { + t[i] = pos; + } + else + { + t[nbNewPoints - i - 1] = 1.0 - pos; + } + + // Commented to prevent bloated output with a casual debug + // MESSAGE("Calculated position " << i << ": " << pos); + } + + // Make points for each calculated value + for(const auto i : t) + { + const double abscissa = i * theLength; + MESSAGE("abscissa: " << abscissa); + + GCPnts_AbscissaPoint Discret(Precision::Confusion(), theC3d, abscissa, f); + if (Discret.IsDone()) + theParams.push_back(Discret.Parameter()); + } + + return true; +} + //============================================================================= /*! * @@ -906,6 +978,10 @@ bool StdMeshers_Regular_1D::computeInternalParameters(SMESH_Mesh & theMesh, theParams); } break; + + case StdMeshers_NumberOfSegments::DT_BetaLaw: + return computeBetaLaw(theC3d, theParams, f, theLength, _value[BETA_IND], nbSegments, theReverse); + case StdMeshers_NumberOfSegments::DT_Regular: eltSize = theLength / double( nbSegments ); break; diff --git a/src/StdMeshers/StdMeshers_Regular_1D.hxx b/src/StdMeshers/StdMeshers_Regular_1D.hxx index b19e81ec5..a4c01c9ef 100644 --- a/src/StdMeshers/StdMeshers_Regular_1D.hxx +++ b/src/StdMeshers/StdMeshers_Regular_1D.hxx @@ -106,6 +106,14 @@ protected: double theLastU, std::list & theParameters ); + bool computeBetaLaw(Adaptor3d_Curve& theC3d, + std::list& theParams, + double f, + double theLength, + double beta, + int nbSegments, + bool theReverse); + /*! * \brief Return StdMeshers_SegmentLengthAroundVertex assigned to vertex */ @@ -117,6 +125,7 @@ protected: enum ValueIndex { SCALE_FACTOR_IND = 0, + BETA_IND = 0, BEG_LENGTH_IND = 0, END_LENGTH_IND = 1, DEFLECTION_IND = 0, diff --git a/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.cxx b/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.cxx index 116df94bc..d60a9a9bd 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.cxx @@ -36,6 +36,7 @@ #include #include #include +#include // IDL includes #include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) @@ -65,6 +66,7 @@ StdMeshersGUI_NbSegmentsCreator::StdMeshersGUI_NbSegmentsCreator() myNbSeg( 0 ), myDistr( 0 ), myScale( 0 ), + myBeta( 0 ), myTable( 0 ), #ifndef DISABLE_PLOT2DVIEWER myPreview( 0 ), @@ -73,6 +75,7 @@ StdMeshersGUI_NbSegmentsCreator::StdMeshersGUI_NbSegmentsCreator() myConvBox( 0 ), myConv( 0 ), myLScale( 0 ), + myLBeta( 0 ), myLTable( 0 ), myLExpr( 0 ), myInfo( 0 ), @@ -96,6 +99,7 @@ bool StdMeshersGUI_NbSegmentsCreator::checkParams( QString& msg ) const bool res = storeParamsToHypo( data_new ); res = myNbSeg->isValid( msg, true ) && res; res = myScale->isValid( msg, true ) && res; + res = myBeta->isValid( msg, true ) && res; if ( !res ) storeParamsToHypo( data_old ); return res; @@ -151,6 +155,7 @@ QFrame* StdMeshersGUI_NbSegmentsCreator::buildFrame() types.append( tr( "SMESH_DISTR_SCALE" ) ); types.append( tr( "SMESH_DISTR_TAB" ) ); types.append( tr( "SMESH_DISTR_EXPR" ) ); + types.append( tr( "SMESH_DISTR_BETALAW" ) ); myDistr->addItems( types ); myGroupLayout->addWidget( myDistr, row, 1 ); row++; @@ -238,6 +243,14 @@ QFrame* StdMeshersGUI_NbSegmentsCreator::buildFrame() lay->setStretchFactor( myReversedEdgesHelper, 1 ); } + // 7) Beta Law distribution + myGroupLayout->addWidget(myLBeta = new QLabel(tr("SMESH_NB_SEGMENTS_BETA_PARAM"), GroupC1), row, 0); + myBeta = new SMESHGUI_SpinBoxForbiddendRange(GroupC1); + myBeta->RangeStepAndValidator(-1E+5, 1E+5, 0.00001, "parametric_precision"); + myBeta->SetForbiddenRange(-1.0, 1.0); + myGroupLayout->addWidget(myBeta, row, 1); + row++; + connect( myNbSeg, SIGNAL( valueChanged( const QString& ) ), this, SLOT( onValueChanged() ) ); connect( myDistr, SIGNAL( activated( int ) ), this, SLOT( onValueChanged() ) ); connect( myTable, SIGNAL( valueChanged( int, int ) ), this, SLOT( onValueChanged() ) ); @@ -271,6 +284,8 @@ void StdMeshersGUI_NbSegmentsCreator::retrieveParams() const myTable->setData( data.myTable ); myExpr->setText( data.myExpr ); + myBeta->setValue(data.myBeta); + if ( dlg() ) dlg()->setMinimumSize( dlg()->minimumSizeHint().width(), dlg()->minimumSizeHint().height() ); } @@ -288,7 +303,8 @@ QString StdMeshersGUI_NbSegmentsCreator::storeParams() const Regular, //!< equidistant distribution Scale, //!< scale distribution TabFunc, //!< distribution with density function presented by table - ExprFunc //!< distribution with density function presented by expression + ExprFunc, //!< distribution with density function presented by expression + BetaLaw //!< distribution with density function presented by expression }; bool hasConv = false; switch ( data.myDistrType ) { @@ -314,6 +330,10 @@ QString StdMeshersGUI_NbSegmentsCreator::storeParams() const valStr += data.myExpr; hasConv = true; break; + + case BetaLaw: + valStr += tr("SMESH_NB_SEGMENTS_BETA_PARAM") + " = " + QString::number(data.myBeta); + break; } if ( hasConv ) { @@ -366,6 +386,7 @@ bool StdMeshersGUI_NbSegmentsCreator::readParamsFromHypo( NbSegmentsHypothesisDa h_data.myExpr = distr==3 ? h->GetExpressionFunction() : "1"; h_data.myConv = distr==2 || distr==3 ? h->ConversionMode() : 1; /*cut negative by default*/ + h_data.myBeta = distr==4 ? h->GetBeta() : 1.01; return true; } @@ -394,7 +415,7 @@ bool StdMeshersGUI_NbSegmentsCreator::storeParamsToHypo( const NbSegmentsHypothe if( distr==2 || distr==3 ) h->SetConversionMode( h_data.myConv ); - if( distr==1 || distr==2 || distr==3 ) { + if( distr==1 || distr==2 || distr==3 || distr == 4) { h->SetReversedEdges( myDirectionWidget->GetListOfIDs() ); h->SetObjectEntry( myDirectionWidget->GetMainShapeEntry() ); } @@ -407,6 +428,12 @@ bool StdMeshersGUI_NbSegmentsCreator::storeParamsToHypo( const NbSegmentsHypothe //setting of function must follow after setConversionMode, because otherwise //the function will be checked with old conversion mode, so that it may occurs //unexpected errors for user + + if(distr == 4) + { + h->SetDistrType(distr); + h->SetBeta(h_data.myBeta); + } } catch(const SALOME::SALOME_Exception& ex) { @@ -425,6 +452,7 @@ bool StdMeshersGUI_NbSegmentsCreator::readParamsFromWidgets( NbSegmentsHypothesi h_data.myDistrType = myDistr->currentIndex(); h_data.myConv = myConv->checkedId(); h_data.myScale = myScale->value(); + h_data.myBeta = myBeta->value(); myTable->data( h_data.myTable ); h_data.myExpr = myExpr->text(); return true; @@ -483,6 +511,11 @@ void StdMeshersGUI_NbSegmentsCreator::onValueChanged() myPreview->setConversion( StdMeshersGUI_DistrPreview::Conversion( myConv->checkedId() ) ); #endif + // Beta Law UI elements + const bool isBetaLaw = distr == 4; + myBeta->setVisible(isBetaLaw); + myLBeta->setVisible(isBetaLaw); + if ( (QtxComboBox*)sender() == myDistr && dlg() ) { QApplication::instance()->processEvents(); myGroupLayout->invalidate(); diff --git a/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.h b/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.h index d873fdae1..67b612ce9 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.h +++ b/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.h @@ -49,15 +49,16 @@ class QGridLayout; class QRadioButton; class StdMeshersGUI_SubShapeSelectorWdg; class StdMeshersGUI_PropagationHelperWdg; +class SMESHGUI_SpinBoxForbiddendRange; typedef struct { int myNbSeg, myDistrType, myConv; double myScale; + double myBeta; SMESH::double_array myTable; QString myName, myExpr; QString myNbSegVarName, myScaleVarName; - } NbSegmentsHypothesisData; class STDMESHERSGUI_EXPORT StdMeshersGUI_NbSegmentsCreator : public StdMeshersGUI_StdHypothesisCreator @@ -87,6 +88,7 @@ private: SalomeApp_IntSpinBox* myNbSeg; QtxComboBox* myDistr; SMESHGUI_SpinBox* myScale; + SMESHGUI_SpinBoxForbiddendRange* myBeta; StdMeshersGUI_DistrTableFrame* myTable; #ifndef DISABLE_PLOT2DVIEWER StdMeshersGUI_DistrPreview* myPreview; @@ -94,7 +96,7 @@ private: QLineEdit *myName, *myExpr; QGroupBox* myConvBox; QButtonGroup* myConv; - QLabel *myLScale, *myLTable, *myLExpr, *myInfo; + QLabel *myLScale, *myLTable, *myLExpr, *myInfo, *myLBeta; QGridLayout* myGroupLayout; int myTableRow, myPreviewRow; //QRadioButton* myCutNeg; diff --git a/src/StdMeshersGUI/StdMeshers_msg_en.ts b/src/StdMeshersGUI/StdMeshers_msg_en.ts index 85a684bc6..8df409d6d 100644 --- a/src/StdMeshersGUI/StdMeshers_msg_en.ts +++ b/src/StdMeshersGUI/StdMeshers_msg_en.ts @@ -154,6 +154,10 @@ Consider creating another hypothesis instead of using this one for this mesh/sub SMESH_DISTR_TAB Distribution with table density + + SMESH_DISTR_BETALAW + Distribution with Beta Law + SMESH_DISTR_TYPE Type of distribution @@ -310,6 +314,10 @@ Consider creating another hypothesis instead of using this one for this mesh/sub SMESH_NB_SEGMENTS_SCALE_PARAM Scale Factor + + SMESH_NB_SEGMENTS_BETA_PARAM + Expansion coefficient + SMESH_NB_SEGMENTS_TITLE Hypothesis Construction diff --git a/src/StdMeshersGUI/StdMeshers_msg_fr.ts b/src/StdMeshersGUI/StdMeshers_msg_fr.ts index 1558c5476..96643ce93 100644 --- a/src/StdMeshersGUI/StdMeshers_msg_fr.ts +++ b/src/StdMeshersGUI/StdMeshers_msg_fr.ts @@ -154,6 +154,10 @@ Veuillez plutôt créer une autre hypothèse à la place de celle-ci pour ce mai SMESH_DISTR_TAB Distribution avec une table de densités + + SMESH_DISTR_BETALAW + Distribution avec la Loi Bêta + SMESH_DISTR_TYPE Type de distribution @@ -306,6 +310,10 @@ Veuillez plutôt créer une autre hypothèse à la place de celle-ci pour ce mai SMESH_NB_SEGMENTS_PARAM Nombre de segments + + SMESH_NB_SEGMENTS_BETA_PARAM + Coefficient de dilatation + SMESH_NB_SEGMENTS_SCALE_PARAM Facteur d'échelle diff --git a/src/StdMeshersGUI/StdMeshers_msg_ja.ts b/src/StdMeshersGUI/StdMeshers_msg_ja.ts index 3e0763809..78eefcc80 100644 --- a/src/StdMeshersGUI/StdMeshers_msg_ja.ts +++ b/src/StdMeshersGUI/StdMeshers_msg_ja.ts @@ -142,6 +142,10 @@ SMESH_DISTR_TAB 密度のテーブル + + SMESH_DISTR_BETALAW + ベータ法による分布 + SMESH_DISTR_TYPE 分布の種類 @@ -294,6 +298,10 @@ SMESH_NB_SEGMENTS_PARAM セグメントの数 + + SMESH_NB_SEGMENTS_BETA_PARAM + 膨張係数 + SMESH_NB_SEGMENTS_SCALE_PARAM スケール ファクター diff --git a/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.cxx b/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.cxx index 5a3a0af40..597f7a5c9 100644 --- a/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.cxx +++ b/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.cxx @@ -234,6 +234,50 @@ CORBA::Double StdMeshers_NumberOfSegments_i::GetScaleFactor() return scale; } +//============================================================================= +/*! + * StdMeshers_NumberOfSegments_i::SetBeta + * + * Set beta coefficient for Beta Law distribution + */ +//============================================================================= + +void StdMeshers_NumberOfSegments_i::SetBeta(CORBA::Double beta) +{ + ASSERT(myBaseImpl); + try { + this->GetImpl()->SetBeta(beta); + // Update Python script + SMESH::TPythonDump() << _this() << ".SetBeta( " << SMESH::TVar(beta) << " )"; + } + catch (SALOME_Exception& S_ex) { + THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM); + } +} + +//============================================================================= +/*! + * StdMeshers_NumberOfSegments_i::GetBeta + * + * Get beta coefficient for Beta Law distribution + */ +//============================================================================= + +CORBA::Double StdMeshers_NumberOfSegments_i::GetBeta() +{ + ASSERT(myBaseImpl); + + double beta = 1.0; + try { + beta = this->GetImpl()->GetBeta(); + } + catch (SALOME_Exception& S_ex) { + THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM); + } + + return beta; +} + //============================================================================= /*! */ diff --git a/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.hxx b/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.hxx index 28e94d0e6..521828a23 100644 --- a/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.hxx +++ b/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.hxx @@ -72,6 +72,11 @@ public: // Get scalar factor CORBA::Double GetScaleFactor(); + // Set beta coefficient for Beta Law distribution + void SetBeta(CORBA::Double beta); + // Get beta coefficient for Beta Law distribution + CORBA::Double GetBeta(); + // Set table function for distribution DT_TabFunc void SetTableFunction(const SMESH::double_array& table); // Get table function for distribution DT_TabFunc diff --git a/test/SMESH_beta_law.py b/test/SMESH_beta_law.py new file mode 100644 index 000000000..db9c8e031 --- /dev/null +++ b/test/SMESH_beta_law.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +import salome + +salome.salome_init() + +### +### GEOM component +### + +from salome.geom import geomBuilder + +geompy = geomBuilder.New() + +p1 = geompy.MakeVertex(0, 0, 0, theName="p1") +p2 = geompy.MakeVertex(.1, 0, 0, theName="p2") +p3 = geompy.MakeVertex(.1, .3, 0, theName="p3") +p4 = geompy.MakeVertex(0, .3, 0, theName="p4") + +p5 = geompy.MakeVertex(-0.05, 0.1, 0, theName="p5") +p6 = geompy.MakeVertex(-0.05, 0.05, 0, theName="p6") + +points = [p1, p2, p3, p4, p5, p6, p1] + +polyline = geompy.MakePolyline(points, theName="polyline") + +face = geompy.MakeFace(polyline, 1, theName="face") + +edges_by_name = {} +for i in range(len(points)-1): + name = "edge_%i"%(i+1) + edge = geompy.GetEdge(face, points[i], points[i+1], theName=name) + edges_by_name[name] = edge + +### +### SMESH component +### + +import SMESH +from salome.smesh import smeshBuilder + +smesh = smeshBuilder.New() + +Mesh_1 = smesh.Mesh(face,'Mesh_1') + +main_algo1D = Mesh_1.Segment() +main_algo1D.NumberOfSegments(5) + +sub_algo1D = Mesh_1.Segment(edges_by_name["edge_1"]) +nb_seg_hyp = sub_algo1D.NumberOfSegments(29) +nb_seg_hyp.SetDistrType( 4 ) +nb_seg_hyp.SetBeta( 1.001 ) + + +sub_algo1D = Mesh_1.Segment(edges_by_name["edge_2"]) +nb_seg_hyp = sub_algo1D.NumberOfSegments(19) + +sub_algo1D = Mesh_1.Segment(edges_by_name["edge_3"]) +nb_seg_hyp = sub_algo1D.NumberOfSegments(29) +nb_seg_hyp.SetDistrType( 4 ) +nb_seg_hyp.SetBeta( -1.001 ) + +sub_algo1D = Mesh_1.Segment(edges_by_name["edge_4"]) +nb_seg_hyp = sub_algo1D.NumberOfSegments(9) + +Quadrangle_2D = Mesh_1.Quadrangle() + +isDone = Mesh_1.Compute() +Mesh_1.CheckCompute() + +gr_edges_by_name = {} +for name, edge in edges_by_name.items(): + gr_edge = Mesh_1.Group(edge) + gr_edges_by_name[name] = gr_edge + +assert Mesh_1.NbEdges() == 5+5+29*2+19+9 +assert Mesh_1.NbQuadrangles() == 551 + +expected_mini = 2.9948449467664952e-05 +expected_maxi = 0.013044372341778604 +mini_1, maxi_1 = Mesh_1.GetMinMax(SMESH.FT_Length, meshPart=gr_edges_by_name["edge_1"]) +mini_3, maxi_3 = Mesh_1.GetMinMax(SMESH.FT_Length, meshPart=gr_edges_by_name["edge_3"]) + +assert abs(expected_mini-mini_1)/mini_1 < 1e-12 +assert abs(expected_mini-mini_3)/mini_3 < 1e-12 + +assert abs(expected_maxi-maxi_1)/maxi_1 < 1e-12 +assert abs(expected_maxi-maxi_3)/maxi_3 < 1e-12 + + +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser() diff --git a/test/SMESH_reg.py b/test/SMESH_reg.py index df568d872..b7c14880a 100644 --- a/test/SMESH_reg.py +++ b/test/SMESH_reg.py @@ -70,7 +70,7 @@ smesh.UpdateStudy() # ---- Creating meshes box = salome.IDToObject(idbox) -names = [ "MeshBoxReg", "MeshBoxScale", "MeshBoxTable", "MeshBoxExpr" ] +names = [ "MeshBoxReg", "MeshBoxScale", "MeshBoxTable", "MeshBoxExpr", "MeshBoxBeta" ] print("-------------------------- Create ", names[0], " mesh") @@ -82,6 +82,9 @@ smesh.SetName(hyp, "NumberOfSegmentsReg") algo = mesh.Triangle() algo.MaxElementArea(2500) +mesh.Compute() +mesh.CheckCompute() + print("-------------------------- Create ", names[1], " mesh") mesh = smesh.Mesh(box, names[1]) algo = mesh.Segment() @@ -92,6 +95,9 @@ smesh.SetName(hyp, "NumberOfSegmentsScale") algo = mesh.Triangle() algo.MaxElementArea(2500) +mesh.Compute() +mesh.CheckCompute() + print("-------------------------- Create ", names[2], " mesh") mesh = smesh.Mesh(box,names[2]) algo = mesh.Segment() @@ -103,6 +109,9 @@ smesh.SetName(hyp, "NumberOfSegmentsTable") algo = mesh.Triangle() algo.MaxElementArea(2500) +mesh.Compute() +mesh.CheckCompute() + print("-------------------------- Create ", names[3], " mesh") mesh = smesh.Mesh(box, names[3]) algo = mesh.Segment() @@ -114,6 +123,25 @@ smesh.SetName(hyp, "NumberOfSegmentsExpr") algo = mesh.Triangle() algo.MaxElementArea(2500) +mesh.Compute() +mesh.CheckCompute() + +print("-------------------------- Create ", names[4], " mesh") +mesh = smesh.Mesh(box, names[4]) +algo = mesh.Segment() +hyp = algo.NumberOfSegments(7) +hyp.SetDistrType(4) +hyp.SetBeta(1.01) +smesh.SetName(hyp, "NumberOfSegmentsBeta") + +quad_2d = mesh.Quadrangle() +quad_2d.SetName("Quadrangle_2D") + +hexa_3d = mesh.Hexahedron() +hexa_3d.SetName("Hexa_3D") + +mesh.Compute() +mesh.CheckCompute() + salome.sg.updateObjBrowser() - diff --git a/test/tests.set b/test/tests.set index 9de76f4a3..9ed691711 100644 --- a/test/tests.set +++ b/test/tests.set @@ -42,6 +42,7 @@ SET(BAD_TESTS PAL_MESH_041_mesh.py PAL_MESH_043_3D.py SMESH_BelongToGeom.py + SMESH_beta_law.py SMESH_box2_tetra.py SMESH_box3_tetra.py SMESH_box_tetra.py