Salome HOME
0020871: EDF 1411 SMESH: Distribution with analytical density crashes Salome
[modules/smesh.git] / src / StdMeshersGUI / StdMeshersGUI_DistrPreview.cxx
1 //  Copyright (C) 2007-2010  CEA/DEN, EDF R&D, 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.
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 // File   : StdMeshersGUI_DistrPreview.cxx
24 // Author : Open CASCADE S.A.S.
25 // SMESH includes
26 //
27 #include "StdMeshersGUI_DistrPreview.h"
28
29 // Qwt includes
30 #include <qwt_plot_curve.h>
31 #include <qwt_plot_marker.h>
32 #include <qwt_plot_grid.h>
33 #include <qwt_symbol.h>
34 #include <qwt_legend.h>
35
36 // OCCT includes
37 #include <Expr_NamedUnknown.hxx>
38 #include <Expr_GeneralExpression.hxx>
39
40 #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100
41 #define NO_CAS_CATCH
42 #endif
43
44 #include <Standard_Failure.hxx>
45
46 #ifdef NO_CAS_CATCH
47 #include <Standard_ErrorHandler.hxx>
48 #endif
49
50 #ifdef WIN32
51 # include <algorithm>
52 #endif
53 #include <math.h>
54
55 #include <Basics_Utils.hxx>
56
57 StdMeshersGUI_DistrPreview::StdMeshersGUI_DistrPreview( QWidget* p, StdMeshers::StdMeshers_NumberOfSegments_ptr h )
58 : QwtPlot( p ),
59   myPoints( 50 ),
60   myIsTable( false ),
61   myVars( 1, 1 ),
62   myValues( 1, 1 ),
63   myConv( CUT_NEGATIVE ),
64   myIsDone( true ),
65   myNbSeg( 1 )
66 {
67   Kernel_Utils::Localizer loc;
68   myHypo = StdMeshers::StdMeshers_NumberOfSegments::_duplicate( h );
69   myVars.ChangeValue( 1 ) = new Expr_NamedUnknown( "t" );
70   myDensity = new QwtPlotCurve( QString() );
71   myDensity->attach( this );
72   myDistr = new QwtPlotCurve( QString() );
73   myDistr->attach( this );
74   myMsg = new QwtPlotMarker();
75   myMsg->attach( this );
76   myMsg->setValue( 0.5, 0.5 );
77   QwtText mt = myMsg->label();
78   mt.setBackgroundPen( QPen( Qt::red, 1 ) );
79   QFont f = mt.font();
80   f.setPointSize( 14 ); f.setBold( true );
81   mt.setFont( f );
82   myMsg->setLabel( mt );
83   myDensity->setPen( QPen( Qt::red, 1 ) );
84
85   QColor dc = Qt::blue;
86   myDistr->setPen( QPen( dc, 1 ) );
87   myDistr->setSymbol( QwtSymbol( QwtSymbol::XCross, QBrush( dc ), QPen( dc ), QSize( 5, 5 ) ) );
88
89   QwtLegend* l = legend();
90   if ( !l ) {
91     l = new QwtLegend( this );
92     l->setFrameStyle( QFrame::Box | QFrame::Sunken );
93   }
94   insertLegend( l, QwtPlot::BottomLegend );
95
96   myDensity->setTitle( tr( "SMESH_DENSITY_FUNC" ) );
97   myDistr->setTitle( tr( "SMESH_DISTR" ) );
98   
99   QwtPlotGrid* aGrid = new QwtPlotGrid();
100   QPen aMajPen = aGrid->majPen();
101   aMajPen.setStyle( Qt::DashLine );
102   aGrid->setPen( aMajPen );
103
104   aGrid->enableX( true );
105   aGrid->enableY( true );
106
107   aGrid->attach( this );
108 }
109
110 StdMeshersGUI_DistrPreview::~StdMeshersGUI_DistrPreview()
111 {
112 }
113
114 bool StdMeshersGUI_DistrPreview::isTableFunc() const
115 {
116   return myIsTable;
117 }
118
119 void StdMeshersGUI_DistrPreview::tableFunc( SMESH::double_array& f ) const
120 {
121   f = myTableFunc;
122 }
123
124 QString StdMeshersGUI_DistrPreview::function() const
125 {
126   return myFunction;
127 }
128
129 int StdMeshersGUI_DistrPreview::nbSeg() const
130 {
131   return myNbSeg;
132 }
133
134 int StdMeshersGUI_DistrPreview::pointsCount() const
135 {
136   return myPoints;
137 }
138
139 void StdMeshersGUI_DistrPreview::setConversion( Conversion conv, const bool upd )
140 {
141   myConv = conv;
142   if( upd )
143     update();
144 }
145
146 bool StdMeshersGUI_DistrPreview::setParams( const QString& func, const int nbSeg, const int points, const bool upd )
147 {
148   myIsTable = false;
149   myTableFunc = SMESH::double_array();
150   myFunction = func.isEmpty() ? "0" : func;
151   myPoints = points>0 ? points : 2;
152   myNbSeg = nbSeg>0 ? nbSeg : 1;
153   bool res = init( func );
154   if( upd )
155     update();
156   return res;
157 }
158
159 bool StdMeshersGUI_DistrPreview::setParams( const SMESH::double_array& f, const int nbSeg, const bool upd )
160 {
161   myIsTable = true;
162   myTableFunc = f;
163   if( myTableFunc.length()%2==1 )
164     myTableFunc.length( myTableFunc.length()-1 );
165
166   myFunction = "0";
167   myPoints = myTableFunc.length()/2;
168   myNbSeg = nbSeg>0 ? nbSeg : 1;
169
170   if( upd )
171     update();
172
173   return myTableFunc.length()>0;
174 }
175
176 bool StdMeshersGUI_DistrPreview::createTable( SMESH::double_array& func )
177 {
178   if( myExpr.IsNull() )
179   {
180     func.length( 0 );
181     return false;
182   }
183
184   const double xmin = 0.0, xmax = 1.0;
185
186   double d = (xmax-xmin)/double(myPoints-1);
187   func.length( 2*myPoints );
188   int err = 0;
189   for( int i=0, j=0; i<myPoints; j++ )
190   {
191     bool ok;
192     double t = xmin + d*j, f = funcValue( t, ok );
193     if( ok )
194     {
195       func[2*i] = t;
196       func[2*i+1] = f;
197       i++;
198     }
199     else
200       err++;
201   }
202   func.length( func.length()-2*err );
203   return err==0;
204 }
205
206 void StdMeshersGUI_DistrPreview::update()
207 {
208   Kernel_Utils::Localizer loc;
209   SMESH::double_array graph, distr;
210   if( isTableFunc() )
211   {
212     myIsDone = true;
213     graph = myTableFunc;
214   }
215   else
216     myIsDone = createTable( graph );
217
218   if( graph.length()>=2 )
219   {
220     StdMeshers::StdMeshers_NumberOfSegments_var h = 
221       StdMeshers::StdMeshers_NumberOfSegments::_narrow( myHypo );
222
223     if( !CORBA::is_nil( h.in() ) )
224     {
225       SMESH::double_array* arr = 0;
226       if( isTableFunc() )
227         arr = h->BuildDistributionTab( myTableFunc, myNbSeg, ( int )myConv );
228       else
229         arr = h->BuildDistributionExpr( myFunction.toLatin1().data(), myNbSeg, ( int )myConv );
230       if( arr )
231       {
232         distr = *arr;
233         delete arr;
234       }
235     }
236   }
237
238   bool correct = graph.length()>=2 && distr.length()>=2;
239   if( !correct )
240   {
241     showError();
242     return;
243   }
244   else
245   {
246     QwtText mt = myMsg->label();
247     mt.setText( QString() );
248     myMsg->setLabel( mt );
249   }
250
251   int size = graph.length()/2;
252   double* x = new double[size], *y = new double[size];
253   double min_x, max_x, min_y, max_y;
254   for( int i=0; i<size; i++ )
255   {
256     x[i] = graph[2*i];
257     y[i] = graph[2*i+1];
258     if( !convert( y[i] ) )
259     {
260       min_x = 0.0; max_x = 1.0; min_y = 0.0; max_y = 1.0;
261       delete[] x; delete[] y;
262       x = y = 0;
263       showError();
264       return;
265     }
266     if ( isinf(y[i]))
267       y[i] = std::numeric_limits<double>::max()/100.;
268 //     if ( y[i] > 1e3 )
269 //       y[i] = 1e3;
270     if( i==0 || y[i]<min_y )
271       min_y = y[i];
272     if( i==0 || y[i]>max_y )
273       max_y = y[i];
274     if( i==0 || x[i]<min_x )
275       min_x = x[i];
276     if( i==0 || x[i]>max_x )
277       max_x = x[i];
278   }
279
280   setAxisScale( myDensity->xAxis(), min_x, max_x );
281   setAxisScale( myDensity->yAxis(),
282 #ifdef WIN32
283     min( 0.0, min_y ),
284     max( 0.0, max_y )
285 #else
286     std::min( 0.0, min_y ),
287     std::max( 0.0, max_y )
288 #endif
289     );
290   myDensity->setData( x, y, size );
291   if( x )
292     delete[] x;
293   if( y )
294     delete[] y;
295   x = y = 0;
296
297   size = distr.length();
298   x = new double[size];
299   y = new double[size];
300   for( int i=0; i<size; i++ )
301   {
302     x[i] = distr[i];
303     y[i] = 0;
304   }
305   myDistr->setData( x, y, size );
306   delete[] x;
307   delete[] y;
308   x = y = 0;
309
310   try {   
311 #ifdef NO_CAS_CATCH
312     OCC_CATCH_SIGNALS;
313 #endif
314     replot();
315   } catch(Standard_Failure) {
316     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
317   }
318 }
319
320 void StdMeshersGUI_DistrPreview::showError()
321 {
322   setAxisScale( myDensity->xAxis(), 0.0, 1.0 );
323   setAxisScale( myDensity->yAxis(), 0.0, 1.0 );
324   myDensity->setData( 0, 0, 0 );
325   myDistr->setData( 0, 0, 0 );
326   QwtText mt = myMsg->label();
327   mt.setText( tr( "SMESH_INVALID_FUNCTION" ) );
328   myMsg->setLabel( mt );
329   replot();
330 }
331
332 bool isCorrectArg( const Handle( Expr_GeneralExpression )& expr )
333 {
334   Handle( Expr_NamedUnknown ) sub = Handle( Expr_NamedUnknown )::DownCast( expr );
335   if( !sub.IsNull() )
336     return sub->GetName()=="t";
337
338   bool res = true;
339   for( int i=1, n=expr->NbSubExpressions(); i<=n && res; i++ )
340   {
341     Handle( Expr_GeneralExpression ) sub = expr->SubExpression( i );
342     Handle( Expr_NamedUnknown ) name = Handle( Expr_NamedUnknown )::DownCast( sub );
343     if( !name.IsNull() )
344     {
345       if( name->GetName()!="t" )
346         res = false;
347     }
348     else
349       res = isCorrectArg( sub );
350   }
351   return res;
352 }
353
354 bool StdMeshersGUI_DistrPreview::init( const QString& str )
355 {
356   Kernel_Utils::Localizer loc;
357   bool parsed_ok = true;
358   try {
359 #ifdef NO_CAS_CATCH
360     OCC_CATCH_SIGNALS;
361 #endif
362     myExpr = ExprIntrp_GenExp::Create();
363     myExpr->Process( ( Standard_CString ) str.toLatin1().data() );
364   } catch(Standard_Failure) {
365     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
366     parsed_ok = false;
367   }
368
369   bool syntax = false, args = false;
370   if( parsed_ok && myExpr->IsDone() )
371   {
372     syntax = true;
373     args = isCorrectArg( myExpr->Expression() );
374   }
375
376   bool res = parsed_ok && syntax && args;
377   if( !res )
378     myExpr.Nullify();
379   return res;
380 }
381
382 double StdMeshersGUI_DistrPreview::funcValue( const double t, bool& ok )
383 {
384   if( myExpr.IsNull() )
385     return 0;
386
387   myValues.ChangeValue( 1 ) = t;
388
389   ok = true;
390   double res = calc( ok );
391
392   return res;
393 }
394
395 double StdMeshersGUI_DistrPreview::calc( bool& ok )
396 {
397   double res = 0.0;
398
399   ok = true;
400   try {   
401 #ifdef NO_CAS_CATCH
402     OCC_CATCH_SIGNALS;
403 #endif
404     res = myExpr->Expression()->Evaluate( myVars, myValues );
405   } catch(Standard_Failure) {
406     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
407     ok = false;
408     res = 0.0;
409   }
410
411   return res;
412 }
413
414 bool StdMeshersGUI_DistrPreview::isDone() const
415 {
416   return myIsDone;
417 }
418
419 bool StdMeshersGUI_DistrPreview::convert( double& v ) const
420 {
421   bool ok = true;
422   switch( myConv )
423   {
424   case EXPONENT:
425     {
426       try { 
427 #ifdef NO_CAS_CATCH
428         OCC_CATCH_SIGNALS;
429 #endif
430         // in StdMeshers_NumberOfSegments.cc
431         // const double PRECISION = 1e-7;
432         //
433         if(v < -7) v = -7.0;
434         v = pow( 10.0, v );
435       } catch(Standard_Failure) {
436         Handle(Standard_Failure) aFail = Standard_Failure::Caught();
437         v = 0.0;
438         ok = false;
439       }
440     }
441     break;
442
443   case CUT_NEGATIVE:
444     if( v<0 )
445       v = 0;
446     break;
447   }
448
449   return ok;
450 }