Salome HOME
Merge branch 'OCCT780'
[modules/smesh.git] / src / StdMeshers / StdMeshers_NumberOfSegments.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //  SMESH SMESH : implementation of SMESH idl descriptions
24 //  File   : StdMeshers_NumberOfSegments.cxx
25 //           Moved here from SMESH_NumberOfSegments.cxx
26 //  Author : Paul RASCLE, EDF
27 //  Module : SMESH
28 //
29 #include "StdMeshers_NumberOfSegments.hxx"
30
31 #include "SMESHDS_Mesh.hxx"
32 #include "SMESHDS_SubMesh.hxx"
33 #include "SMESH_Comment.hxx"
34 #include "SMESH_Mesh.hxx"
35 #include "StdMeshers_Distribution.hxx"
36
37 #include <ExprIntrp_GenExp.hxx>
38 #include <Expr_Array1OfNamedUnknown.hxx>
39 #include <Expr_NamedUnknown.hxx>
40 #include <TColStd_Array1OfReal.hxx>
41 #include <TCollection_AsciiString.hxx>
42 #include <TopExp.hxx>
43 #include <TopTools_IndexedMapOfShape.hxx>
44
45 #include <Standard_Failure.hxx>
46 #include <Standard_ErrorHandler.hxx>
47
48 #include <Basics_Utils.hxx>
49
50 using namespace StdMeshers;
51 using namespace std;
52
53 const double PRECISION = 1e-7;
54
55 //=============================================================================
56 /*!
57  *  
58  */
59 //=============================================================================
60
61 StdMeshers_NumberOfSegments::StdMeshers_NumberOfSegments(int         hypId,
62                                                          SMESH_Gen * gen)
63   : StdMeshers_Reversible1D(hypId, gen),
64     _numberOfSegments(15),//issue 19923
65     _distrType(DT_Regular),
66     _scaleFactor(1.),
67     _convMode(1)  //cut negative by default
68 {
69   _name = "NumberOfSegments";
70   _param_algo_dim = 1;
71 }
72
73 //=============================================================================
74 /*!
75  *  
76  */
77 //=============================================================================
78
79 StdMeshers_NumberOfSegments::~StdMeshers_NumberOfSegments()
80 {
81 }
82
83 //=============================================================================
84 /*!
85  *  
86  */
87 //=============================================================================
88 const vector<double>&
89 StdMeshers_NumberOfSegments::BuildDistributionExpr( const char* expr,int nbSeg,int conv )
90 {
91   if( !buildDistribution( TCollection_AsciiString( ( Standard_CString )expr ), conv, 0.0, 1.0, nbSeg, _distr, 1E-4 ) )
92     _distr.resize( 0 );
93   return _distr;
94 }
95
96 const vector<double>&
97 StdMeshers_NumberOfSegments::BuildDistributionTab( const vector<double>& tab,
98                                                    int nbSeg,
99                                                    int conv )
100 {
101   if( !buildDistribution( tab, conv, 0.0, 1.0, nbSeg, _distr, 1E-4 ) )
102     _distr.resize( 0 );
103   return _distr;
104 }
105
106 //=============================================================================
107 /*!
108  *  
109  */
110 //=============================================================================
111
112 void StdMeshers_NumberOfSegments::SetNumberOfSegments(smIdType segmentsNumber)
113 {
114   smIdType oldNumberOfSegments = _numberOfSegments;
115   if (segmentsNumber <= 0)
116     throw SALOME_Exception(LOCALIZED("number of segments must be positive"));
117   _numberOfSegments = segmentsNumber;
118
119   if (oldNumberOfSegments != _numberOfSegments)
120     NotifySubMeshesHypothesisModification();
121 }
122
123 //=============================================================================
124 /*!
125  *  
126  */
127 //=============================================================================
128
129 smIdType StdMeshers_NumberOfSegments::GetNumberOfSegments() const
130 {
131   return _numberOfSegments;
132 }
133
134 //================================================================================
135 /*!
136  * 
137  */
138 //================================================================================
139
140 void StdMeshers_NumberOfSegments::SetDistrType(DistrType typ)
141 {
142   if (!IsValidDistrType(typ))
143     throw SALOME_Exception(LOCALIZED("distribution type is out of range"));
144
145   if (typ != _distrType)
146   {
147     _distrType = typ;
148     NotifySubMeshesHypothesisModification();
149   }
150 }
151
152 //================================================================================
153 /*!
154  * 
155  */
156 //================================================================================
157
158 StdMeshers_NumberOfSegments::DistrType StdMeshers_NumberOfSegments::GetDistrType() const
159 {
160   return _distrType;
161 }
162
163 //================================================================================
164 /*!
165  * 
166  */
167 //================================================================================
168
169 bool StdMeshers_NumberOfSegments::IsValidDistrType(int distrType) const
170 {
171   // DistrType is sequential, so we can just check against its first and last values 
172   return distrType >= DT_Regular && distrType <= DT_BetaLaw;
173 }
174
175 //================================================================================
176 /*!
177  * 
178  */
179 //================================================================================
180
181 void StdMeshers_NumberOfSegments::SetScaleFactor(double scaleFactor)
182 {
183   if (scaleFactor < PRECISION)
184     throw SALOME_Exception(LOCALIZED("scale factor must be positive"));
185
186   if (_distrType != DT_Scale)
187     _distrType = DT_Scale;
188
189 //  commented by mpa for IPAL 52986
190 //  if ( fabs(scaleFactor - 1.0) < PRECISION )
191 //    _distrType = DT_Regular;
192
193   if ( fabs(_scaleFactor - scaleFactor) > PRECISION )
194   {
195     _scaleFactor = scaleFactor;
196     NotifySubMeshesHypothesisModification();
197   }
198 }
199
200 //================================================================================
201 /*!
202  *
203  */
204 //================================================================================
205
206 double StdMeshers_NumberOfSegments::GetScaleFactor() const
207 {
208   if (_distrType != DT_Scale)
209     throw SALOME_Exception(LOCALIZED("not a scale distribution"));
210   return _scaleFactor;
211 }
212
213 //================================================================================
214 /*!
215  * 
216  */
217 //================================================================================
218
219 void StdMeshers_NumberOfSegments::SetBeta(double beta)
220 {
221   if (_distrType != DT_BetaLaw)
222     throw SALOME_Exception(LOCALIZED("not a Beta Law distribution"));
223
224   const double diff = fabs(fabs(_beta) - fabs(beta));
225   if (diff <= PRECISION)
226   {
227     // Check for a special case where we have values with
228     // equal base but opposite signs like -1.01 and 1.01
229     if (std::signbit(_beta) == std::signbit(beta))
230       return;
231   }
232
233   _beta = beta;
234   NotifySubMeshesHypothesisModification();
235 }
236
237 //================================================================================
238 /*!
239  *
240  */
241 //================================================================================
242
243 double StdMeshers_NumberOfSegments::GetBeta() const
244 {
245   if (_distrType != DT_BetaLaw)
246     throw SALOME_Exception(LOCALIZED("not a Beta Law distribution"));
247   return _beta;
248 }
249
250 //================================================================================
251 /*!
252  *
253  */
254 //================================================================================
255
256 void StdMeshers_NumberOfSegments::SetTableFunction(const vector<double>& table)
257 {
258   if (_distrType != DT_TabFunc)
259     _distrType = DT_TabFunc;
260   //throw SALOME_Exception(LOCALIZED("not a table function distribution"));
261   if ( (table.size() % 2) != 0 )
262     throw SALOME_Exception(LOCALIZED("odd size of vector of table function"));
263
264   double prev = -PRECISION;
265   bool isSame = table.size() == _table.size();
266
267   bool pos = false;
268   for ( size_t i = 0; i < table.size() / 2; i++ )
269   {
270     double par = table[i*2];
271     double val = table[i*2+1];
272     if( _convMode==0 )
273     {
274       try {
275         OCC_CATCH_SIGNALS;
276         val = pow( 10.0, val );
277       }
278       catch(Standard_Failure&) {
279         throw SALOME_Exception( LOCALIZED( "invalid value"));
280         return;
281       }
282     }
283     else if( _convMode==1 && val<0.0 )
284       val = 0.0;
285
286     if ( par < 0 || par > 1)
287       throw SALOME_Exception(LOCALIZED("parameter of table function is out of range [0,1]"));
288     if ( fabs(par-prev) < PRECISION )
289       throw SALOME_Exception(LOCALIZED("two parameters are the same"));
290     if ( val < 0 )
291       throw SALOME_Exception(LOCALIZED("value of table function is not positive"));
292     if( val > PRECISION )
293       pos = true;
294     if (isSame)
295     {
296       double oldpar = _table[i*2];
297       double oldval = _table[i*2+1];
298       if ( fabs(par - oldpar) > PRECISION || fabs(val - oldval) > PRECISION )
299         isSame = false;
300     }
301     prev = par;
302   }
303
304   if( !pos )
305     throw SALOME_Exception(LOCALIZED("value of table function is not positive"));
306
307   if ( pos && !isSame )
308   {
309     _table = table;
310     NotifySubMeshesHypothesisModification();
311   }
312 }
313
314 //================================================================================
315 /*!
316  *
317  */
318 //================================================================================
319
320 const vector<double>& StdMeshers_NumberOfSegments::GetTableFunction() const
321 {
322   if (_distrType != DT_TabFunc)
323     throw SALOME_Exception(LOCALIZED("not a table function distribution"));
324   return _table;
325 }
326
327 //================================================================================
328 /*! check if only 't' is unknown variable in expression
329  */
330 //================================================================================
331 bool isCorrectArg( const Handle( Expr_GeneralExpression )& expr )
332 {
333   Handle( Expr_NamedUnknown ) sub = Handle( Expr_NamedUnknown )::DownCast( expr );
334   if( !sub.IsNull() )
335     return sub->GetName()=="t";
336
337   bool res = true;
338   for( int i=1, n=expr->NbSubExpressions(); i<=n && res; i++ )
339   {
340     Handle( Expr_GeneralExpression ) sub = expr->SubExpression( i );
341     Handle( Expr_NamedUnknown ) name = Handle( Expr_NamedUnknown )::DownCast( sub );
342     if( !name.IsNull() )
343     {
344       if( name->GetName()!="t" )
345         res = false;
346     }
347     else
348       res = isCorrectArg( sub );
349   }
350   return res;
351 }
352
353 //================================================================================
354 /*! this function parses the expression 'str' in order to check if syntax is correct
355  *  ( result in 'syntax' ) and if only 't' is unknown variable in expression ( result in 'args' )
356  */
357 //================================================================================
358 bool process( const TCollection_AsciiString& str, int convMode,
359               bool& syntax, bool& args,
360               bool& non_neg, bool& non_zero,
361               bool& singulars, double& sing_point )
362 {
363   Kernel_Utils::Localizer loc;
364
365   bool parsed_ok = true;
366   Handle( ExprIntrp_GenExp ) myExpr;
367   try {
368     OCC_CATCH_SIGNALS;
369     myExpr = ExprIntrp_GenExp::Create();
370     myExpr->Process( str.ToCString() );
371   } catch(Standard_Failure&) {
372     parsed_ok = false;
373   }
374
375   syntax = false;
376   args = false;
377   if( parsed_ok && myExpr->IsDone() )
378   {
379     syntax = true;
380     args = isCorrectArg( myExpr->Expression() );
381   }
382
383   bool res = parsed_ok && syntax && args;
384   if( !res )
385     myExpr.Nullify();
386
387   non_neg = true;
388   singulars = false;
389   non_zero = false;
390
391   if( res )
392   {
393     FunctionExpr f( str.ToCString(), convMode );
394     const int max = 500;
395     for( int i=0; i<=max; i++ )
396     {
397       double t = double(i)/double(max), val;
398       if( !f.value( t, val ) )
399       {
400         sing_point = t;
401         singulars = true;
402         break;
403       }
404       if( val<0 )
405       {
406         non_neg = false;
407         break;
408       }
409       if( val>PRECISION )
410         non_zero = true;
411     }
412   }
413
414   return res && non_neg && non_zero && ( !singulars );
415 }
416
417 //================================================================================
418 /*!
419  * 
420  */
421 //================================================================================
422
423 void StdMeshers_NumberOfSegments::SetExpressionFunction(const char* expr)
424 {
425   if (_distrType != DT_ExprFunc)
426     _distrType = DT_ExprFunc;
427
428   string func = CheckExpressionFunction( expr, _convMode );
429   if( _func != func )
430   {
431     _func = func;
432     NotifySubMeshesHypothesisModification();
433   }
434 }
435
436 //=======================================================================
437 //function : CheckExpressionFunction
438 //purpose  : Checks validity of  the expression of the function f(t), e.g. "sin(t)".
439 //           In case of validity returns a cleaned expression
440 //=======================================================================
441
442 std::string
443 StdMeshers_NumberOfSegments::CheckExpressionFunction( const std::string& expr,
444                                                       const int          convMode)
445 {
446   // remove white spaces
447   TCollection_AsciiString str((Standard_CString)expr.c_str());
448   str.RemoveAll(' ');
449   str.RemoveAll('\t');
450   str.RemoveAll('\r');
451   str.RemoveAll('\n');
452
453   bool syntax, args, non_neg, singulars, non_zero;
454   double sing_point;
455   bool res = process( str, convMode, syntax, args, non_neg, non_zero, singulars, sing_point );
456   if( !res )
457   {
458     if( !syntax )
459       throw SALOME_Exception(SMESH_Comment("invalid expression syntax: ") << str );
460     if( !args )
461       throw SALOME_Exception(LOCALIZED("only 't' may be used as function argument"));
462     if( !non_neg )
463       throw SALOME_Exception(LOCALIZED("only non-negative function can be used"));
464     if( singulars )
465     {
466       char buf[1024];
467       sprintf( buf, "Function has singular point in %.3f", sing_point );
468       throw SALOME_Exception( buf );
469     }
470     if( !non_zero )
471       throw SALOME_Exception(LOCALIZED("f(t)=0 cannot be used"));
472   }
473  
474   return str.ToCString();
475 }
476
477 //================================================================================
478 /*!
479  * 
480  */
481 //================================================================================
482
483 const char* StdMeshers_NumberOfSegments::GetExpressionFunction() const
484 {
485   if (_distrType != DT_ExprFunc)
486     throw SALOME_Exception(LOCALIZED("not an expression function distribution"));
487   return _func.c_str();
488 }
489
490 //================================================================================
491 /*!
492  * 
493  */
494 //================================================================================
495
496 void StdMeshers_NumberOfSegments::SetConversionMode( int conv )
497 {
498 //   if (_distrType != DT_TabFunc && _distrType != DT_ExprFunc)
499 //     throw SALOME_Exception(LOCALIZED("not a functional distribution"));
500
501   if( conv != _convMode )
502   {
503     _convMode = conv;
504     NotifySubMeshesHypothesisModification();
505   }
506 }
507
508 //================================================================================
509 /*!
510  * 
511  */
512 //================================================================================
513
514 int StdMeshers_NumberOfSegments::ConversionMode() const
515 {
516 //   if (_distrType != DT_TabFunc && _distrType != DT_ExprFunc)
517 //     throw SALOME_Exception(LOCALIZED("not a functional distribution"));
518   return _convMode;
519 }
520
521 //=============================================================================
522 /*!
523  *  
524  */
525 //=============================================================================
526
527 ostream & StdMeshers_NumberOfSegments::SaveTo(ostream & save)
528 {
529   size_t listSize = _edgeIDs.size();
530   save << _numberOfSegments << " " << (int)_distrType;
531   switch (_distrType)
532   {
533   case DT_Scale:
534     save << " " << _scaleFactor;
535     break;
536   case DT_TabFunc:
537     save << " " << _table.size();
538     for ( size_t i = 0; i < _table.size(); i++ )
539       save << " " << _table[i];
540     break;
541   case DT_ExprFunc:
542     save << " " << _func;
543     break;
544   case DT_BetaLaw:
545     save << " " << _beta;
546     break;
547   case DT_Regular:
548   default:
549     break;
550   }
551
552   if (_distrType == DT_TabFunc || _distrType == DT_ExprFunc)
553     save << " " << _convMode;
554
555   if ( _distrType != DT_Regular && listSize > 0 ) {
556     save << " " << listSize;
557     for ( size_t i = 0; i < listSize; i++ )
558       save << " " << _edgeIDs[i];
559     save << " " << _objEntry;
560   }
561   
562   return save;
563 }
564
565 //=============================================================================
566 /*!
567  *  
568  */
569 //=============================================================================
570
571 istream & StdMeshers_NumberOfSegments::LoadFrom(istream & load)
572 {
573   bool isOK = true;
574   int a;
575
576   // read number of segments
577   isOK = static_cast<bool>(load >> a);
578   if (isOK)
579     _numberOfSegments = a;
580   else
581     load.clear(ios::badbit | load.rdstate());
582
583   // read second stored value. It can be two variants here:
584   // 1. If the hypothesis is stored in old format (nb.segments and scale factor),
585   //    we wait here the scale factor, which is double.
586   // 2. If the hypothesis is stored in new format
587   //    (nb.segments, distr.type, some other params.),
588   //    we wait here the distribution type, which is integer
589   double scale_factor;
590   isOK = static_cast<bool>(load >> scale_factor);
591   a = (int)scale_factor;
592
593   // try to interpret distribution type,
594   // supposing that this hypothesis was written in the new format
595   if (isOK)
596   {
597     if (!IsValidDistrType(a))
598       _distrType = DT_Regular;
599     else
600       _distrType = (DistrType) a;
601   }
602   else
603     load.clear(ios::badbit | load.rdstate());
604
605   // parameters of distribution
606   double b;
607   switch (_distrType)
608   {
609   case DT_Scale:
610     {
611       isOK = static_cast<bool>(load >> b);
612       if (isOK)
613         _scaleFactor = b;
614       else
615       {
616         load.clear(ios::badbit | load.rdstate());
617         // this can mean, that the hypothesis is stored in old format
618         _distrType = DT_Regular;
619         _scaleFactor = scale_factor;
620       }
621     }
622     break;
623   case DT_TabFunc:
624     {
625       isOK = static_cast<bool>(load >> a);
626       if (isOK)
627       {
628         _table.resize(a, 0.);
629         for ( size_t i=0; i < _table.size(); i++ )
630         {
631           isOK = static_cast<bool>(load >> b);
632           if (isOK)
633             _table[i] = b;
634           else
635             load.clear(ios::badbit | load.rdstate());
636         }
637       }
638       else
639       {
640         load.clear(ios::badbit | load.rdstate());
641         // this can mean, that the hypothesis is stored in old format
642         _distrType = DT_Regular;
643         _scaleFactor = scale_factor;
644       }
645     }
646     break;
647   case DT_ExprFunc:
648     {
649       string str;
650       isOK = static_cast<bool>(load >> str);
651       if (isOK)
652         _func = str;
653       else
654       {
655         load.clear(ios::badbit | load.rdstate());
656         // this can mean, that the hypothesis is stored in old format
657         _distrType = DT_Regular;
658         _scaleFactor = scale_factor;
659       }
660     }
661     break;
662
663   case DT_BetaLaw:
664     {
665       isOK = static_cast<bool>(load >> b);
666       if (isOK)
667         _beta = b;
668       else
669       {
670         load.clear(ios::badbit | load.rdstate());
671         // this can mean, that the hypothesis is stored in old format
672         _distrType = DT_Regular;
673         _scaleFactor = scale_factor;
674       }
675     }
676     break;
677
678   case DT_Regular:
679   default:
680     break;
681   }
682
683   if (_distrType == DT_TabFunc || _distrType == DT_ExprFunc)
684   {
685     isOK = static_cast<bool>(load >> a);
686     if (isOK)
687       _convMode = a;
688     else
689       load.clear(ios::badbit | load.rdstate());
690   }
691
692   // load reversed edges IDs
693   int intVal;
694   isOK = static_cast<bool>(load >> intVal);
695   if ( isOK && _distrType != DT_Regular && intVal > 0 ) {
696     _edgeIDs.reserve( intVal );
697     for ( size_t i = 0; i < _edgeIDs.capacity() && isOK; i++) {
698       isOK = static_cast<bool>(load >> intVal);
699       if ( isOK ) _edgeIDs.push_back( intVal );
700     }
701     isOK = static_cast<bool>(load >> _objEntry);
702   }
703
704   return load;
705 }
706
707 //=============================================================================
708 /*!
709  *
710  */
711 //=============================================================================
712
713 ostream & operator <<(ostream & save, StdMeshers_NumberOfSegments & hyp)
714 {
715   return hyp.SaveTo( save );
716 }
717
718 //=============================================================================
719 /*!
720  *  
721  */
722 //=============================================================================
723
724 istream & operator >>(istream & load, StdMeshers_NumberOfSegments & hyp)
725 {
726   return hyp.LoadFrom( load );
727 }
728
729 //================================================================================
730 /*!
731  * \brief Initialize number of segments by the mesh built on the geometry
732  * \param theMesh - the built mesh
733  * \param theShape - the geometry of interest
734  * \retval bool - true if parameter values have been successfully defined
735  */
736 //================================================================================
737
738 bool StdMeshers_NumberOfSegments::SetParametersByMesh(const SMESH_Mesh*   theMesh,
739                                                       const TopoDS_Shape& theShape)
740 {
741   if ( !theMesh || theShape.IsNull() )
742     return false;
743
744   _numberOfSegments = 0;
745   _distrType = DT_Regular;
746
747   int nbEdges = 0;
748   TopTools_IndexedMapOfShape edgeMap;
749   TopExp::MapShapes( theShape, TopAbs_EDGE, edgeMap );
750   SMESHDS_Mesh* aMeshDS = const_cast< SMESH_Mesh* >( theMesh )->GetMeshDS();
751   for ( int i = 1; i <= edgeMap.Extent(); ++i )
752   {
753     // get current segment length
754     SMESHDS_SubMesh * eSubMesh = aMeshDS->MeshElements( edgeMap( i ));
755     if ( eSubMesh && eSubMesh->NbElements())
756       _numberOfSegments += eSubMesh->NbElements();
757
758     ++nbEdges;
759   }
760   if ( nbEdges )
761     _numberOfSegments /= nbEdges;
762
763   if (_numberOfSegments == 0) _numberOfSegments = 1;
764
765   return nbEdges;
766 }
767 //================================================================================
768 /*!
769  * \brief Initialize my parameter values by default parameters.
770  *  \retval bool - true if parameter values have been successfully defined
771  */
772 //================================================================================
773
774 bool StdMeshers_NumberOfSegments::SetParametersByDefaults(const TDefaults&  dflts,
775                                                           const SMESH_Mesh* /*theMesh*/)
776 {
777   return (_numberOfSegments = dflts._nbSegments );
778 }