Salome HOME
Regression of Offset dialog: result shape not published
[modules/geom.git] / src / MeasureGUI / MeasureGUI_ShapeStatisticsDlg.cxx
1 // Copyright (C) 2015-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File   : MeasureGUI_ShapeStatisticsDlg.cxx
21 // Author : Alexander KOVALEV, OPEN CASCADE S.A.S.
22
23 // internal includes
24 #include "MeasureGUI_ShapeStatisticsDlg.h"
25
26 // GEOM includes
27 #include <GEOMBase.h>
28 #include <GEOMUtils_ShapeStatistics.hxx>
29 #include <GeometryGUI.h>
30 #include <DlgRef.h>
31
32 // GUI includes
33 #include <SUIT_Desktop.h>
34 #include <SUIT_MessageBox.h>
35 #include <SUIT_Session.h>
36 #include <SUIT_ResourceMgr.h>
37 #include <SUIT_ViewManager.h>
38 #include <SUIT_ViewWindow.h>
39
40 #include <LightApp_SelectionMgr.h>
41
42 #include <SalomeApp_Application.h>
43 #include <SalomeApp_Study.h>
44
45 #include <Plot2d_Histogram.h>
46 #include <Plot2d_ViewFrame.h>
47 #include <Plot2d_ViewModel.h>
48 #include <Plot2d_ViewWindow.h>
49
50 // Qt includes
51 #include <QIcon>
52 #include <QGridLayout>
53 #include <QPushButton>
54 #include <QLabel>
55
56 // Qtx includes
57 #include <QtxValidator.h>
58
59 // OCC includes
60 #include <TopoDS_Shape.hxx>
61
62 #include <GEOMImpl_Types.hxx>
63
64 //===========================================================================
65 // class    : MeasureGUI_ShapeStatisticsDlg()
66 //===========================================================================
67 MeasureGUI_ShapeStatisticsDlg::MeasureGUI_ShapeStatisticsDlg( QWidget* parent, TopoDS_Shape aShape, TopAbs_ShapeEnum aSubShapeType )
68 : GEOMBase_Helper( SUIT_Session::session()->activeApplication()->desktop() ),
69   QDialog( parent ),
70   myHistogram ( 0 )
71 {
72   myShapes.push_back( aShape );
73
74   QIcon iconSelect( SUIT_Session::session()->resourceMgr()->loadPixmap( "GEOM", tr( "ICON_SELECT" ) ) );
75
76   setWindowTitle( tr( "GEOM_SHAPE_STATISTICS" ) );
77   setAttribute( Qt::WA_DeleteOnClose );
78
79   myApp = dynamic_cast< SalomeApp_Application* >( SUIT_Session::session()->activeApplication() );
80
81   QVBoxLayout* topLayout = new QVBoxLayout( this );
82
83   QGridLayout* settingsLayout = new QGridLayout();
84
85   /**********************   Selected Objects    **********************/
86
87   QLabel* objsLabel = new QLabel( tr( "GEOM_SELECTED_OBJECTS" ), this );
88   QPushButton* selBtn = new QPushButton( this );
89   selBtn->setIcon( iconSelect );
90   myEditMainShape = new QLineEdit( this );
91   myEditMainShape->setReadOnly(true);
92
93   settingsLayout->addWidget( objsLabel, 0, 0 );
94   settingsLayout->addWidget( selBtn, 0, 1 );
95   settingsLayout->addWidget( myEditMainShape, 0, 2 );
96
97   if ( !aShape.IsNull() ) {
98     objsLabel->hide();
99     selBtn->hide();
100     myEditMainShape->hide();
101   }
102
103   /**********************   Type    **********************/
104
105   QLabel* typeLabel = new QLabel( tr( "GEOM_SHAPE_STATISTICS_TYPE" ), this );
106   myCBTypes = new QtxComboBox( this );
107   myCBTypes->setCleared( true );
108   if ( aSubShapeType != TopAbs_SHAPE ) {
109     fillTypes( aSubShapeType == TopAbs_EDGE,
110                aSubShapeType == TopAbs_FACE,
111                aSubShapeType == TopAbs_SOLID );
112     myCBTypes->setEnabled( false );
113   }
114
115   settingsLayout->addWidget( typeLabel, 1, 0 );
116   settingsLayout->addWidget( myCBTypes, 1, 2 );
117
118   /**********************   Number of intervals    **********************/
119
120   QLabel* nbIntervalsLabel = new QLabel( tr( "GEOM_SHAPE_STATISTICS_NB_INTERVALS" ), this );
121   myNbIntervals = new QtxIntSpinBox( 1, 1000, 1, this );
122   myNbIntervals->setValue( 10 );
123
124   settingsLayout->addWidget( nbIntervalsLabel, 2, 0 );
125   settingsLayout->addWidget( myNbIntervals, 2, 2 );
126
127   /**********************   Scalar Range    **********************/
128
129   myScalarRangeBox = new QGroupBox( tr( "GEOM_SHAPE_STATISTICS_SCALAR_RANGE" ), this );
130   myScalarRangeBox->setCheckable( true );
131   myScalarRangeBox->setChecked( false );
132   QLabel* minLabel = new QLabel( tr( "GEOM_SHAPE_STATISTICS_MIN" ), this );
133   myMin = new QLineEdit( this );
134   QtxDoubleValidator* aValid = new QtxDoubleValidator( this );
135   aValid->setBottom( 0.0 );
136   myMin->setValidator( aValid );
137   QLabel* maxLabel = new QLabel( tr( "GEOM_SHAPE_STATISTICS_MAX" ), this );
138   myMax = new QLineEdit( this );
139   myMax->setValidator( aValid );
140
141   QPushButton* buttonCompute = new QPushButton( tr( "GEOM_SHAPE_STATISTICS_COMPUTE" ), this );
142   connect( buttonCompute, SIGNAL( clicked() ), this, SLOT( clickOnCompute() ) );
143
144   QGridLayout* scalarRangeLayout = new QGridLayout();
145   scalarRangeLayout->setMargin( 11 ); settingsLayout->setSpacing( 6 );
146
147   scalarRangeLayout->addWidget( minLabel, 0, 0 );
148   scalarRangeLayout->addWidget( myMin,    0, 1 );
149   scalarRangeLayout->addWidget( maxLabel, 1, 0 );
150   scalarRangeLayout->addWidget( myMax,    1, 1 );
151   scalarRangeLayout->addWidget( buttonCompute, 0, 2, 2, 1 );
152
153   myScalarRangeBox->setLayout( scalarRangeLayout );
154
155   /**********************   Buttons    **********************/
156
157   myButtonPlot   = new QPushButton( tr( "GEOM_PLOT_DISTRIBUTION" ), this );
158   myButtonPlot->setDefault( true );
159   myButtonCreateGr = new QPushButton( tr( "GEOM_SHAPE_STATISTICS_CREATE_GROUPS" ), this );
160   QPushButton* buttonClose  = new QPushButton( tr( "GEOM_BUT_CLOSE" ), this );
161   QPushButton* buttonHelp   = new QPushButton( tr( "GEOM_BUT_HELP" ), this );
162
163   QHBoxLayout* buttonsLayout = new QHBoxLayout();
164   buttonsLayout->addWidget( myButtonPlot );
165   buttonsLayout->addWidget( myButtonCreateGr );
166   buttonsLayout->addWidget( buttonClose );
167   buttonsLayout->addWidget( buttonHelp );
168
169   if ( !aShape.IsNull() ) {
170     myButtonCreateGr->hide();
171   }
172   /**********************   Layouting    **********************/
173
174   topLayout->addLayout( settingsLayout );
175   topLayout->addWidget( myScalarRangeBox );
176   topLayout->addLayout( buttonsLayout );
177
178   // Signals and slots connections
179
180   connect( selBtn, SIGNAL( clicked() ), this, SLOT( onEditMainShape() ) );
181
182   connect( myButtonPlot,     SIGNAL( clicked() ), this, SLOT( clickOnPlot() ) );
183   connect( myButtonCreateGr, SIGNAL( clicked() ), this, SLOT( clickOnCreateGroups() ) );
184
185   connect( buttonClose,    SIGNAL( clicked() ), this, SLOT( reject() ) );
186   connect( buttonHelp,     SIGNAL( clicked() ), this, SLOT( clickOnHelp() ) );
187
188   connect(myApp->selectionMgr(),
189           SIGNAL(currentSelectionChanged()), this, SLOT(onEditMainShape()));
190
191   if ( aShape.IsNull() )
192     onEditMainShape();
193 }
194
195 //===========================================================================
196 // function : ~MeasureGUI_ShapeStatisticsDlg()
197 // purpose  : Destroys the object and frees any allocated resources
198 //===========================================================================
199 MeasureGUI_ShapeStatisticsDlg::~MeasureGUI_ShapeStatisticsDlg()
200 {
201 }
202
203 //=================================================================================
204 // function : createOperation
205 // purpose  :
206 //=================================================================================
207 GEOM::GEOM_IOperations_ptr MeasureGUI_ShapeStatisticsDlg::createOperation()
208 {
209   return getGeomEngine()->GetIGroupOperations(getStudyId());
210 }
211
212 #define RETURN_WITH_MSG(a, b)                   \
213   if (!(a)) {                                   \
214     theMessage += (b);                          \
215     return false;                               \
216   }
217
218 //================================================================
219 // Function : getFather
220 // Purpose  : Get father object for object to be added in study
221 //            (called with addInStudy method)
222 //================================================================
223 GEOM::GEOM_Object_ptr MeasureGUI_ShapeStatisticsDlg::getFather(GEOM::GEOM_Object_ptr theObj)
224 {
225   GEOM::GEOM_Object_var aFatherObj;
226   if (theObj->GetType() == GEOM_GROUP) {
227     GEOM::GEOM_IGroupOperations_var anOper = GEOM::GEOM_IGroupOperations::_narrow(getOperation());
228     aFatherObj = anOper->GetMainShape(theObj);
229   }
230   return aFatherObj._retn();
231 }
232
233 //=================================================================================
234 // function : getSourceObjects
235 // purpose  : virtual method to get source objects
236 //=================================================================================
237 QList<GEOM::GeomObjPtr> MeasureGUI_ShapeStatisticsDlg::getSourceObjects()
238 {
239   QList<GEOM::GeomObjPtr> res;
240   res << myMainObj;
241   return res;
242 }
243
244 //=================================================================================
245 // function : onEditMainShape()
246 // purpose  : called when selection button was clicked
247 //=================================================================================
248 void MeasureGUI_ShapeStatisticsDlg::onEditMainShape()
249 {
250   // restore initial parameters for dialog box
251   myEditMainShape->setText("");
252   myEditMainShape->setFocus();
253
254   //get shapes from selection
255   QList<GEOM::GeomObjPtr> selShapes = getSelected( TopAbs_SHAPE, -1 );
256
257   myButtonPlot->setEnabled( !selShapes.isEmpty() );
258   myButtonCreateGr->setEnabled( selShapes.count() == 1 );
259
260   if ( !selShapes.isEmpty() ) {
261     if ( selShapes.count() == 1 )
262       myMainObj = selShapes[0];
263     QString aName = selShapes.count() > 1 ? QString( "%1_objects").arg( selShapes.count() ) : GEOMBase::GetName( myMainObj.get() );
264     myEditMainShape->setText( aName );
265   }
266
267   updateTypes( selShapes );
268 }
269
270 //=================================================================================
271 // function : currentType()
272 // purpose  : returns currently selected type of shapes in 'Type' combobox
273 //=================================================================================
274 void MeasureGUI_ShapeStatisticsDlg::fillTypes( bool hasEdges, bool hasFaces, bool hasSolids )
275 {
276   if ( hasEdges )
277     myCBTypes->addItem( tr("GEOM_SHAPE_STATISTICS_LENGTH"), (int)TopAbs_EDGE );
278   if ( hasFaces )
279     myCBTypes->addItem( tr("GEOM_SHAPE_STATISTICS_AREA"), (int)TopAbs_FACE );
280   if ( hasSolids )
281     myCBTypes->addItem( tr("GEOM_SHAPE_STATISTICS_VOLUME"), (int)TopAbs_SOLID );
282
283   myCBTypes->setEnabled( myCBTypes->count() > 0 );
284 }
285
286 //=================================================================================
287 // function : updateTypes()
288 // purpose  : update 'Type' combobox with available types
289 //=================================================================================
290 void MeasureGUI_ShapeStatisticsDlg::updateTypes( QList<GEOM::GeomObjPtr> theShapes )
291 {
292   myCBTypes->clear();
293   myCBTypes->setEnabled( false );
294
295   int hasEdges = -1, hasFaces = -1, hasSolids = -1;
296
297   myShapes.clear();
298   // get types of the shapes and its sub-shapes
299   foreach( GEOM::GeomObjPtr aShapePtr, theShapes ) {
300     if ( !aShapePtr )
301       return;
302
303     TopoDS_Shape aShape;
304     if ( !GEOMBase::GetShape( aShapePtr.get(), aShape ) || aShape.IsNull() )
305       return;
306
307     myShapes.push_back( aShape );
308
309     GEOM::ListOfLong_var aSubShapes;
310     GEOM::GEOM_IShapesOperations_var aShOp = getGeomEngine()->GetIShapesOperations( getStudyId() );
311     if ( hasEdges != 0 )
312       hasEdges = aShOp->NumberOfSubShapes( aShapePtr.get(), TopAbs_EDGE ) > 0;
313     if ( hasFaces != 0 )
314       hasFaces = aShOp->NumberOfSubShapes( aShapePtr.get(), TopAbs_FACE ) > 0;
315     if ( hasSolids != 0 )
316       hasSolids = aShOp->NumberOfSubShapes( aShapePtr.get(), TopAbs_SOLID ) > 0;
317   }
318   fillTypes( hasEdges, hasFaces, hasSolids );
319 }
320
321 //=================================================================================
322 // function : currentType()
323 // purpose  : returns currently selected type of shapes in 'Type' combobox
324 //=================================================================================
325 TopAbs_ShapeEnum MeasureGUI_ShapeStatisticsDlg::currentType()
326 {
327   return (TopAbs_ShapeEnum)( myCBTypes->itemData( myCBTypes->currentIndex() ).toInt() );
328 }
329
330 //=================================================================================
331 // function : clickOnPlot()
332 // purpose  : called when Plot button was clicked
333 //=================================================================================
334 bool MeasureGUI_ShapeStatisticsDlg::isValid(QString& theMessage)
335 {
336   if ( myScalarRangeBox->isChecked() ) {
337     RETURN_WITH_MSG( !myMin->text().isEmpty(), tr("GEOM_SHAPE_STATISTICS_MIN_ERROR") );
338     RETURN_WITH_MSG( !myMax->text().isEmpty(), tr("GEOM_SHAPE_STATISTICS_MAX_ERROR") );
339     RETURN_WITH_MSG( myMin->text().toDouble() <= myMax->text().toDouble(), tr("GEOM_SHAPE_STATISTICS_MIN_MAX_ERROR") );
340   }
341   return true;
342 }
343 //=================================================================================
344 // function : clickOnPlot()
345 // purpose  : called when Plot button was clicked
346 //=================================================================================
347 void MeasureGUI_ShapeStatisticsDlg::clickOnPlot()
348 {
349   GEOMUtils::Range aRange;
350   if ( myScalarRangeBox->isChecked() ) {
351     QString msg;
352     if ( !isValid( msg ) ) {
353       showError( msg );
354       return;
355     }
356     aRange.min = myMin->text().toDouble();
357     aRange.max = myMax->text().toDouble();
358   } else {
359     aRange.min = -1.0; // flag that range is empty
360     aRange.max = -1.0; // flag that range is empty
361   }
362
363   GEOMUtils::Distribution aShapesDistr =
364     GEOMUtils::ComputeDistribution( myShapes, currentType(), myNbIntervals->value(), aRange );
365
366   QList<double> xVals, yVals;
367   double width = -1, min = -1;
368   double xmin = 1e+32, xmax = 0.0, ymax = 0.0;
369   int i=0;
370   GEOMUtils::Distribution::const_iterator it;
371   for (it = aShapesDistr.begin(); it != aShapesDistr.end(); it++) {
372     GEOMUtils::Range ran = *it;
373     if ( width < 0 ) width = ran.max - ran.min; // bar width
374     if ( min < 0 ) min = ran.min; // global min
375     xVals << width / 2. + i*width + min; // get a middle of bar
376     yVals << ran.count;
377     // get global boundary max values
378     if ( ran.min < xmin ) xmin = ran.min;
379     if ( ran.max > xmax ) xmax = ran.max;
380     if ( ran.count > ymax ) ymax = ran.count;
381     i++;
382   }
383
384   // plot the computed distribution
385   SUIT_ViewManager* aViewManager = myApp->getViewManager( Plot2d_Viewer::Type(), true ); // create if necessary
386   if( !aViewManager )
387     return;
388   Plot2d_ViewWindow* aViewWnd = dynamic_cast<Plot2d_ViewWindow*>(aViewManager->getActiveView());
389   if( !aViewWnd )
390     return;
391   Plot2d_ViewFrame* aPlot = aViewWnd->getViewFrame();
392   if ( !aPlot )
393     return;
394
395   aPlot->EraseAll();
396
397   // create or reuse histogram
398   if( !myHistogram )
399     myHistogram = new Plot2d_Histogram();
400   else
401     myHistogram->clearAllPoints();
402   // set histogram parameters
403   myHistogram->setData( xVals, yVals );
404   if ( width != 0.0 )
405     myHistogram->setWidth( width );
406   myHistogram->setAutoAssign(true);
407   myHistogram->setName( myEditMainShape->text() );
408   myHistogram->setHorTitle( myCBTypes->currentText() );
409   myHistogram->setVerTitle( tr("GEOM_SHAPE_STATISTICS_DISTRIBUTION_NB_ENT") );
410   myHistogram->setColor( QColor(0, 85, 0) );
411   // display histogram
412   aPlot->displayObject( myHistogram, true );
413   if ( width == 0.0 ) // only one X value
414     aPlot->fitAll();
415   else
416     aPlot->fitData( 0, xmin, xmax, 0.0, ymax );
417 }
418
419 //=================================================================================
420 // function : clickOnCompute()
421 // purpose  : called when Compute button was clicked
422 //=================================================================================
423 void MeasureGUI_ShapeStatisticsDlg::clickOnCompute()
424 {
425   GEOMUtils::Range aRange;
426   aRange.min = -1.0; // flag that range is empty
427   aRange.max = -1.0; // flag that range is empty
428   std::map<int,double> measures = GEOMUtils::ComputeMeasures( myShapes, currentType(), aRange );
429   if ( measures.size() != 0 ) {
430     SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
431     int aPrecision = resMgr->integerValue( "Geometry", "length_precision", 6 );
432     myMin->setText( DlgRef::PrintDoubleValue( aRange.min, aPrecision ) );
433     myMax->setText( DlgRef::PrintDoubleValue( aRange.max, aPrecision ) );
434   }
435 }
436
437 //=================================================================================
438 // function : clickOnCreateGroups()
439 // purpose  : called when Create Groups button was clicked
440 //=================================================================================
441 void MeasureGUI_ShapeStatisticsDlg::clickOnCreateGroups()
442 {
443   onAccept(false, false, false);
444 }
445
446 //=================================================================================
447 // function : execute(ObjectList& objects)
448 // purpose  :
449 //=================================================================================
450 bool MeasureGUI_ShapeStatisticsDlg::execute(ObjectList& objects)
451 {
452   if ( myMainObj.isNull() )
453     return false;
454
455   GEOM::GroupOpPtr anOper = GEOM::GEOM_IGroupOperations::_narrow(getOperation());
456
457   GEOMUtils::Range aRange;
458   if ( myScalarRangeBox->isChecked() ) {
459     QString msg;
460     if ( !isValid( msg ) ) {
461       showError( msg );
462       return false;
463     }
464     aRange.min = myMin->text().toDouble();
465     aRange.max = myMax->text().toDouble();
466   } else {
467     aRange.min = -1.0; // flag that range is empty
468     aRange.max = -1.0; // flag that range is empty
469   }
470
471   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
472   int aPrecision = resMgr->integerValue( "Geometry", "length_precision", 6 );
473   QString aTypePrefix = myCBTypes->currentText().replace(' ', '_');
474   QString objIOR, aMin, aMax, aGroupName;
475   //SalomeApp_Study* study = getStudy();
476
477   GEOMUtils::Distribution aShapesDistr =
478     GEOMUtils::ComputeDistribution( myShapes, currentType(), myNbIntervals->value(), aRange );
479
480   int nbGroups = 0;
481
482   GEOMUtils::Distribution::const_iterator it;
483   for (it = aShapesDistr.begin(); it != aShapesDistr.end(); it++) {
484     std::list<long> idList = (*it).indices;
485     int nn = idList.size();
486     if ( nn > 0 ) {
487       GEOM::ListOfLong_var aNewList = new GEOM::ListOfLong;
488       aNewList->length(nn);
489       int ii = 0;
490       std::list<long>::const_iterator id_it;
491       for ( id_it = idList.begin(); id_it != idList.end(); id_it++ ) {
492         aNewList[ii++] = *id_it;
493       }
494
495       // Create an empty group
496       GEOM::GEOM_Object_var aGroup;
497       aGroup = anOper->CreateGroup( myMainObj.get(), currentType() );
498
499       if (CORBA::is_nil(aGroup) || !anOper->IsDone())
500         return false;
501
502       // Add sub-shapes into group
503       anOper->UnionIDs(aGroup, aNewList);
504       if (!anOper->IsDone())
505         return false;
506
507       // publish group
508       aMin = DlgRef::PrintDoubleValue( (*it).min, aPrecision );
509       aMax = DlgRef::PrintDoubleValue( (*it).max, aPrecision );
510       aGroupName =  aTypePrefix + "_" + aMin + "_" + aMax;
511       GEOMBase::PublishSubObject( aGroup, aGroupName );
512
513       // this is needed just to avoid error message
514       objects.push_back(aGroup._retn());
515
516       nbGroups++;
517     }
518   }
519
520   SUIT_MessageBox::information( this, tr( "INF_INFO" ), tr( "GEOM_MSG_GROUPS_CREATED" ).arg( nbGroups ) );
521
522   return true;
523 }
524
525 //=================================================================================
526 // function : clickOnHelp()
527 // purpose  : called when Help button was clicked
528 //=================================================================================
529 void MeasureGUI_ShapeStatisticsDlg::clickOnHelp()
530 {
531   GeometryGUI* aGeomGUI = dynamic_cast<GeometryGUI*>( myApp->module( "Geometry" ) );
532   myApp->onHelpContextModule( aGeomGUI ? myApp->moduleName( aGeomGUI->moduleName() ) : QString(""), "shape_statistics_operation_page.html" );
533 }