Salome HOME
Merge remote branch 'origin/V7_4_BR'
[modules/smesh.git] / src / StdMeshersGUI / StdMeshersGUI_CartesianParamCreator.cxx
1 // Copyright (C) 2007-2014  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, 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 // File   : StdMeshersGUI_CartesianParamCreator.cxx
23 // Author : Open CASCADE S.A.S.
24
25 // SMESH includes
26 #include "StdMeshersGUI_CartesianParamCreator.h"
27
28 #include "SMESHGUI.h"
29 #include "SMESHGUI_Utils.h"
30 #include "SMESHGUI_VTKUtils.h"
31 #include "SMESHGUI_HypothesesUtils.h"
32 #include "SMESHGUI_SpinBox.h"
33 #include "SMESHGUI_MeshEditPreview.h"
34
35 // IDL includes
36 #include CORBA_SERVER_HEADER(SMESH_BasicHypothesis)
37
38 // SALOME GUI includes
39 #include <LightApp_SelectionMgr.h>
40 #include <QtxComboBox.h>
41 #include <SALOME_InteractiveObject.hxx>
42 #include <SALOME_ListIO.hxx>
43 #include <SALOME_ListIteratorOfListIO.hxx>
44 #include <SUIT_ResourceMgr.h>
45 #include <SalomeApp_IntSpinBox.h>
46 #include <SalomeApp_Tools.h>
47
48 #include <GEOMBase.h>
49
50 #include <BRepBndLib.hxx>
51 #include <Bnd_Box.hxx>
52 #include <TopoDS_Iterator.hxx>
53 #include <TopoDS_Shape.hxx>
54 #include <gp_Pnt.hxx>
55
56 // Qt includes
57 #include <QAbstractItemModel>
58 #include <QApplication>
59 #include <QButtonGroup>
60 #include <QCheckBox>
61 #include <QFontMetrics>
62 #include <QGridLayout>
63 #include <QGroupBox>
64 #include <QHBoxLayout>
65 #include <QLabel>
66 #include <QLineEdit>
67 #include <QListWidget>
68 #include <QModelIndex>
69 #include <QPushButton>
70 #include <QRadioButton>
71 #include <QString>
72 #include <QStyleOptionViewItem>
73 #include <QTabWidget>
74 #include <QTreeWidget>
75 #include <QTreeWidgetItem>
76 #include <QVBoxLayout>
77
78 #define SPACING 6
79 #define MARGIN  11
80
81 namespace StdMeshersGUI
82 {
83   enum { COORD_BUT = 0, SPACING_BUT };
84
85   //================================================================================
86   /*!
87    * \brief get spacing definition from a tree item
88    */
89   //================================================================================
90
91   void getFromItem(QTreeWidgetItem * item, double& t0, double& t1, QString& fun )
92   {
93     if ( item )
94     {
95       t0 = item->text( 0 ).split(' ')[0].toDouble();
96       t1 = item->data( 0, Qt::UserRole ).toDouble();
97       fun = item->text( 1 );
98     }
99   }
100
101   //================================================================================
102   /*!
103    * \brief set spacing definition to a tree item
104    */
105   //================================================================================
106
107   QTreeWidgetItem* setToItem(double t0, double t1, const QString& fun, QTreeWidgetItem * item)
108   {
109     if ( !item ) item = new QTreeWidgetItem;
110     item->setText( 0, QString( "%1 - %2" ).arg( t0 ).arg( t1 ));
111     item->setData( 0, Qt::UserRole, t1 );
112     item->setText( 1, fun );
113     item->setFlags( item->flags() | Qt::ItemIsEditable );
114     return item;
115   }
116
117   //================================================================================
118   /*!
119    * \brief Retrieves coordinate value from a list item
120    */
121   //================================================================================
122
123   double coordFromItem( QListWidgetItem * item )
124   {
125     return item ? item->data( Qt::UserRole ).toDouble() : 0;
126   }
127
128   //================================================================================
129   /*!
130    * \brief Sets coordinate value to a list item
131    */
132   //================================================================================
133
134   QListWidgetItem* coordToItem( double coord, QListWidgetItem * item )
135   {
136     if ( !item ) item = new QListWidgetItem;
137     item->setText( QString::number( coord ));
138     item->setData( Qt::UserRole, coord );
139     item->setFlags( item->flags() | Qt::ItemIsEditable );
140     return item;
141   }
142
143   //================================================================================
144   /*!
145    * \brief Constructor
146    * \param theParent - Parent widget for this tab
147    * 
148    * Makes tab's look and feel
149    */
150   //================================================================================
151
152   GridAxisTab::GridAxisTab( QWidget* theParent,const int axisIndex ):
153     QFrame( theParent ), myAxisIndex( axisIndex )
154   {
155     // 1) Grid definition mode
156     myModeGroup = new QButtonGroup( this );
157     QGroupBox* modeBox = new QGroupBox( tr( "GRID_DEF_MODE" ), this );
158     QHBoxLayout* modeLay = new QHBoxLayout( modeBox );
159     modeLay->setMargin( MARGIN );
160     modeLay->setSpacing( SPACING );
161
162     QRadioButton* coordModeBtn = new QRadioButton( tr( "SMESH_COORDINATES" ), modeBox );
163     QRadioButton* spacModeBtn  = new QRadioButton( tr( "SPACING" ), modeBox );
164
165     modeLay->addWidget( coordModeBtn );
166     modeLay->addWidget( spacModeBtn );
167     myModeGroup->addButton( coordModeBtn, COORD_BUT );
168     myModeGroup->addButton( spacModeBtn,  SPACING_BUT );
169
170     // 2) Buttons + Step
171     myInsertBtn = new QPushButton( tr("INSERT"), this);
172     myDeleteBtn = new QPushButton( tr("SMESH_BUT_DELETE"), this);
173
174     myStepLabel = new QLabel( tr("COORD_STEP"));
175     myStepSpin  = new SMESHGUI_SpinBox( this );
176     myStepSpin->setAcceptNames( false ); // No Notebook variables allowed
177     myStepSpin->RangeStepAndValidator();
178     myStepSpin->SetStep( 1. );
179     myStepSpin->SetValue( myStep = 1. );
180
181     // 3) Coodrinates/Spacing group
182     QFrame*    csFrame = new QFrame( this );
183     QVBoxLayout* scLay = new QVBoxLayout( csFrame );
184     scLay->setMargin( 0 );
185     scLay->setSpacing( SPACING );
186
187     // 3.1) Spacing
188     mySpacingTreeWdg = new QTreeWidget( csFrame );
189     mySpacingTreeWdg->setColumnCount(2);
190     mySpacingTreeWdg->setHeaderLabels( QStringList() << tr( "SMESH_RANGE" ) << QString( "f(t)" ));
191     mySpacingTreeWdg->setColumnWidth( 1, 40 );
192     mySpacingTreeWdg->setColumnWidth( 2, 30 );
193     mySpacingTreeWdg->setItemDelegate( new LineDelegate( mySpacingTreeWdg ));
194     scLay->addWidget( mySpacingTreeWdg );
195
196     // 3.2) Coordinates
197     myCoordList = new QListWidget( csFrame );
198     myCoordList->setItemDelegate( new LineDelegate( myCoordList ));
199     scLay->addWidget( myCoordList );
200
201     // layouting
202
203     QGridLayout* axisTabLayout = new QGridLayout( this );
204     axisTabLayout->setMargin( MARGIN );
205     axisTabLayout->setSpacing( SPACING );
206
207     axisTabLayout->addWidget( modeBox    , 0, 0, 1, 3 );
208     axisTabLayout->addWidget( myInsertBtn  , 1, 0, 1, 2 );
209     axisTabLayout->addWidget( myDeleteBtn  , 2, 0, 1, 2 );
210     axisTabLayout->addWidget( myStepLabel, 3, 0 );
211     axisTabLayout->addWidget( myStepSpin , 3, 1 );
212     axisTabLayout->addWidget( csFrame    , 1, 2, 4, 1 );
213
214     axisTabLayout->setRowStretch( 4, 1 );
215
216     // signals
217     connect( myInsertBtn,      SIGNAL( clicked() ),             SLOT( onInsert() ));
218     connect( myDeleteBtn,      SIGNAL( clicked() ),             SLOT( onDelete() ));
219     connect( myModeGroup,      SIGNAL( buttonClicked ( int )),  SLOT( onMode(int)));
220     connect( myModeGroup,      SIGNAL( buttonClicked ( int )),  SIGNAL( gridModeChanged(int)));
221     connect( mySpacingTreeWdg, SIGNAL( itemSelectionChanged()), SLOT( updateButtons() ));
222     connect( myCoordList,      SIGNAL( itemSelectionChanged()), SLOT( updateButtons() ));
223     connect( myStepSpin,       SIGNAL( valueChanged(double)),   SLOT( onStepChange() ));
224   }
225
226   //================================================================================
227   /*!
228    * \brief SLOT onInsert
229    */
230   //================================================================================
231
232   void GridAxisTab::onInsert()
233   {
234     if ( isGridBySpacing() )
235     {
236       QTreeWidgetItem * item = mySpacingTreeWdg->currentItem();
237       if ( !item ) item = mySpacingTreeWdg->topLevelItem( 0 );
238       int i = mySpacingTreeWdg->indexOfTopLevelItem( item );
239
240       double t0, t1; QString fun;
241       getFromItem( item, t0, t1, fun );
242       double t = 0.5 * ( t0 + t1 );
243       setToItem( t0, t, fun, item );
244
245       item = setToItem( t, t1, fun );
246       if ( i == mySpacingTreeWdg->topLevelItemCount()-1 )
247         mySpacingTreeWdg->addTopLevelItem( item );
248       else
249         mySpacingTreeWdg->insertTopLevelItem( i+1, item );
250       mySpacingTreeWdg->setCurrentItem( item );
251     }
252     else
253     {
254       if ( myCoordList->count() == 0 )
255       {
256         myCoordList->addItem( coordToItem( 0 ));
257       }
258       else
259       {
260         double coord = coordFromItem( myCoordList->currentItem() ) + myStep;
261         int i = myCoordList->currentRow();
262         while ( i > 0 && coordFromItem( myCoordList->item( i-1 )) > coord )
263           --i;
264         while ( i < myCoordList->count() && coordFromItem( myCoordList->item( i )) < coord )
265           ++i;
266         const double tol = 1e-6;
267         const bool isSame = 
268           ( i < myCoordList->count() && coordFromItem( myCoordList->item( i )) - coord < tol ) ||
269           ( i > 0 && coord - coordFromItem( myCoordList->item( i-1 )) < tol );
270         if ( !isSame )
271           myCoordList->insertItem( i, coordToItem( coord ));
272         else if ( myStep < 0 )
273           --i;
274         myCoordList->setCurrentRow( i );
275       }
276     }
277     updateButtons();
278   }
279
280   //================================================================================
281   /*!
282    * \brief SLOT onDelete
283    */
284   //================================================================================
285
286   void GridAxisTab::onDelete()
287   {
288     if ( isGridBySpacing() )
289     {
290       QList<QTreeWidgetItem *> selItems = mySpacingTreeWdg->selectedItems();
291       QTreeWidgetItem * item;
292       foreach ( item, selItems )
293       {
294         int i = mySpacingTreeWdg->indexOfTopLevelItem( item );
295         if ( i == 0 ) continue; 
296         QTreeWidgetItem* prevItem = mySpacingTreeWdg->topLevelItem( i-1 );
297
298         double t0, t1, t2; QString fun;
299         getFromItem( item, t1, t2, fun );
300         getFromItem( prevItem, t0, t1, fun );
301         delete item;
302
303         setToItem( t0, t2, fun, prevItem );
304       }
305     }
306     else
307     {
308       if ( myCoordList->count() > 2 )
309         if ( QListWidgetItem * item = myCoordList->currentItem() )
310           delete item;
311     }
312     updateButtons();
313   }
314
315   //================================================================================
316   /*!
317    * \brief SLOT onMode
318    */
319   //================================================================================
320
321   void GridAxisTab::onMode(int isSpacing)
322   {
323     mySpacingTreeWdg->setShown( isSpacing );
324     myCoordList->setShown( !isSpacing );
325     myStepSpin->setShown( !isSpacing );
326     myStepLabel->setShown( !isSpacing );
327     if ( isSpacing )
328     {
329       if ( mySpacingTreeWdg->topLevelItemCount() == 0 )
330       {
331         QString spacing( "1" );
332         if ( myCoordList->count() > 1 )
333         {
334           double c1 = coordFromItem( myCoordList->item( 1 ));
335           double c0 = coordFromItem( myCoordList->item( 0 ));
336           spacing = QString::number( c1 - c0 );
337         }
338         mySpacingTreeWdg->addTopLevelItem( setToItem( 0., 1., spacing ) );
339       }
340       //myCoordList->clear();
341     }
342     else
343     {
344       //mySpacingTreeWdg->clear();
345       if ( myCoordList->count() == 0 )
346         myCoordList->addItem( coordToItem( 0 ));
347     }
348     updateButtons();
349   }
350
351   //================================================================================
352   /*!
353    * \brief SLOT onStepChange
354    */
355   //================================================================================
356
357   void GridAxisTab::onStepChange()
358   {
359     if ( fabs( myStepSpin->GetValue() ) < 1e-100 )
360     {
361       double delta = myStepSpin->singleStep() * ( myStep > myStepSpin->GetValue() ? -1 : +1 );
362       myStepSpin->SetValue( myStepSpin->GetValue() + delta );
363     }
364     myStep = myStepSpin->GetValue();
365   }
366
367   //================================================================================
368   /*!
369    * \brief Enables/disables buttons
370    */
371   //================================================================================
372
373   void GridAxisTab::updateButtons()
374   {
375     bool insertEnable = false, deleteEnable = false;
376     if ( isGridBySpacing() )
377     {
378       insertEnable = true;
379       const int nbSelected = mySpacingTreeWdg->selectedItems().count();
380       if ( nbSelected > 0 )
381       {
382         // we delete a current range by uniting it with the previous
383         int i = mySpacingTreeWdg->indexOfTopLevelItem(  mySpacingTreeWdg->currentItem() );
384         deleteEnable = ( i > 0 );
385       }
386     }
387     else
388     {
389       const int nbSelected = myCoordList->selectedItems().count();
390       insertEnable = ( nbSelected || myCoordList->count() < 2 );
391       deleteEnable = ( nbSelected && myCoordList->count() > 2 );
392     }
393     myInsertBtn->setEnabled( insertEnable );
394     myDeleteBtn->setEnabled( deleteEnable );
395   }
396
397   //================================================================================
398   /*!
399    * \brief Inserts coordinates into myCoordList
400    */
401   //================================================================================
402
403   void GridAxisTab::setCoordinates( SMESH::double_array_var coords )
404   {
405     myCoordList->clear();
406     for ( size_t i = 0; i < coords->length(); ++i )
407       myCoordList->addItem( coordToItem( coords[i] ));
408
409     myModeGroup->button( COORD_BUT )->setChecked( true );
410     onMode( COORD_BUT );
411   }
412
413   //================================================================================
414   /*!
415    * \brief Sets spacing got from hypothesis
416    */
417   //================================================================================
418
419   void GridAxisTab::setSpacing( SMESH::string_array_var funs, SMESH::double_array_var points )
420   {
421     mySpacingTreeWdg->clear();
422     if ( funs->length() == points->length() - 1 )
423     {
424       for ( size_t i = 1; i < points->length(); ++i )
425         mySpacingTreeWdg->addTopLevelItem
426           ( setToItem( points[i-1], points[i], (const char*) funs[i-1] ));
427     }
428     myModeGroup->button( SPACING_BUT )->setChecked( true );
429     onMode( SPACING_BUT );
430   }
431
432   //================================================================================
433   /*!
434    * \brief Checks grid definintion mode
435    */
436   //================================================================================
437
438   bool GridAxisTab::isGridBySpacing() const
439   {
440     return ( myModeGroup->checkedId() == SPACING_BUT );
441   }
442
443   //================================================================================
444   /*!
445    * \brief Returns coordinates to set to a hypothesis
446    */
447   //================================================================================
448
449   SMESH::double_array* GridAxisTab::getCoordinates()
450   {
451     SMESH::double_array_var coords = new SMESH::double_array;
452     coords->length( myCoordList->count() );
453     for ( size_t i = 0; i < coords->length(); ++i )
454       coords[i] = coordFromItem( myCoordList->item( i ) );
455
456     return coords._retn();
457   }
458
459   //================================================================================
460   /*!
461    * \brief Returms spacing to set to a hypothesis
462    */
463   //================================================================================
464
465   void GridAxisTab::getSpacing(SMESH::string_array_out funs,
466                                SMESH::double_array_out points) const
467   {
468     funs =  new SMESH::string_array();
469     points = new SMESH::double_array();
470     funs->length( mySpacingTreeWdg->topLevelItemCount() );
471     points->length( mySpacingTreeWdg->topLevelItemCount() + 1 );
472     double t0, t1; QString fun;
473     for ( size_t i = 0; i < funs->length(); ++i )
474     {
475       QTreeWidgetItem* item = mySpacingTreeWdg->topLevelItem( i );
476       getFromItem( item, t0, t1, fun );
477       points[i] = t0;
478       funs[i] = fun.toLatin1().constData();
479     }
480     points[ points->length()-1 ] = 1.0;
481   }
482
483
484   //================================================================================
485   /*!
486    * \brief Verifies parameters
487    */
488   //================================================================================
489
490   bool GridAxisTab::checkParams(QString& msg, SMESH::SMESH_Hypothesis_var& hyp) const
491   {
492     if ( isGridBySpacing() )
493     {
494       if ( mySpacingTreeWdg->topLevelItemCount() == 0 )
495         return false; // how could it be?
496       StdMeshers::StdMeshers_CartesianParameters3D_var h =
497         StdMeshers::StdMeshers_CartesianParameters3D::_narrow( hyp );
498       SMESH::string_array_var funs;
499       SMESH::double_array_var points;
500       getSpacing( funs.out(), points.out() );
501       try {
502         const char* axisName[3] = { "X", "Y", "Z" };
503         SMESH::double_array_var coords =
504           h->ComputeCoordinates(0.,1., funs, points, axisName[ myAxisIndex ]);
505       }
506       catch ( const SALOME::SALOME_Exception& ex ) {
507         msg = (const char*) ex.details.text;
508         return false;
509       }
510     }
511     else
512     {
513       return myCoordList->count() > 1;
514     }
515     return true;
516   }
517
518   //================================================================================
519   /*!
520    * \brief LineDelegate constructor
521    */
522   //================================================================================
523
524   LineDelegate::LineDelegate( QWidget* parent ):
525     QItemDelegate( parent ),
526     mySpacingTreeWdg( qobject_cast<QTreeWidget*>( parent )),
527     myCoordList( qobject_cast<QListWidget*>( parent ))
528   {
529   }
530
531   //================================================================================
532   /*!
533    * \brief Creates an editor depending on a current item
534    */
535   //================================================================================
536
537   QWidget* LineDelegate::createEditor( QWidget*                    parent,
538                                        const QStyleOptionViewItem& opt,
539                                        const QModelIndex&          index) const
540   {
541     QWidget* w = 0;
542     if ( mySpacingTreeWdg )
543     {
544       if ( index.column() == 0 &&
545            index.row() != mySpacingTreeWdg->topLevelItemCount()-1 )
546       {
547         SMESHGUI_SpinBox* sb = new SMESHGUI_SpinBox( parent );
548         sb->setAcceptNames( false ); // No Notebook variables allowed
549         sb->setFrame( false );
550         w = sb;
551       }
552       if ( index.column() == 1 ) {
553         w = new QLineEdit( parent );
554       }
555     }
556     else
557     {
558       SMESHGUI_SpinBox* sb = new SMESHGUI_SpinBox( parent );
559       sb->setAcceptNames( false ); // No Notebook variables allowed
560       sb->setFrame( false );
561       const double tol = 1e-5;
562       double from = index.row() ? coordFromItem( myCoordList->item( index.row()-1 ))+tol : -1e+6;
563       double to = index.row() == myCoordList->count()-1 ? 1e+6 : coordFromItem( myCoordList->item( index.row()+1 ))-tol;
564       sb->RangeStepAndValidator( from, to, 0.01 );
565       w = sb;
566     }
567     return w;
568   }
569
570   //================================================================================
571   /*!
572    * \brief Limit value range in the spin of a neighbor range
573    */
574   //================================================================================
575
576   void LineDelegate::setEditorData ( QWidget * editor, const QModelIndex & index ) const
577   {
578     if ( mySpacingTreeWdg && index.column() == 0 )
579     {
580       double t0, t1, t2=1.0; QString fun;
581       QTreeWidgetItem* item = mySpacingTreeWdg->topLevelItem( index.row() );
582       getFromItem( item, t0, t1, fun );
583       if ( index.row() != mySpacingTreeWdg->topLevelItemCount()-1 )
584       {
585         item = mySpacingTreeWdg->topLevelItem( index.row()+1 );
586         getFromItem( item, t1, t2, fun );
587       }
588       const double tol = 1e-3;
589       SMESHGUI_SpinBox* sb = qobject_cast<SMESHGUI_SpinBox*>( editor );
590       sb->RangeStepAndValidator( t0 + tol, t2 - tol, 0.01 );
591       sb->SetValue( t1 );
592     }
593     else
594     {
595       QItemDelegate::setEditorData( editor, index );
596     }
597   }
598
599   //================================================================================
600   /*!
601    * \brief
602    */
603   //================================================================================
604
605   void LineDelegate::setModelData( QWidget*            editor,
606                                    QAbstractItemModel* model,
607                                    const QModelIndex&  index ) const
608   {
609     if ( mySpacingTreeWdg )
610     {
611       if ( index.column() == 0 )
612       {
613         if ( index.row() != mySpacingTreeWdg->topLevelItemCount()-1 )
614         {
615           SMESHGUI_SpinBox* sb = qobject_cast<SMESHGUI_SpinBox*>( editor );
616           double t0, t1, t = sb->GetValue(); QString fun;
617
618           QTreeWidgetItem* item = mySpacingTreeWdg->topLevelItem( index.row() );
619           getFromItem( item, t0, t1, fun );
620           setToItem( t0, t, fun, item );
621
622           item = mySpacingTreeWdg->topLevelItem( index.row() + 1 );
623           getFromItem( item, t0, t1, fun );
624           setToItem( t, t1, fun, item );
625         }
626       }
627       else if ( !qobject_cast<QLineEdit*>(editor)->text().trimmed().isEmpty() )
628       {
629         QItemDelegate::setModelData( editor, model, index );
630       }
631     }
632     else
633     {
634       SMESHGUI_SpinBox* sb = qobject_cast<SMESHGUI_SpinBox*>( editor );
635       coordToItem( sb->GetValue(), myCoordList->item( index.row() ));
636     }
637   }
638
639 } // namespace StdMeshersGUI
640
641 namespace
642 {
643   const double theAngTol = M_PI / 180.;
644
645   //================================================================================
646   /*!
647    * \brief Set variables to groups of spin boxes
648    */
649   //================================================================================
650
651   void setText( const QString& vars, SMESHGUI_SpinBox** spins )
652   {
653     QStringList varList = vars.split( ':' );
654     for ( int i = 0; i < 3 && i < varList.count(); ++i )
655       if ( !varList[i].isEmpty() )
656         spins[i]->setText( varList[i] );
657   }
658   
659   //================================================================================
660   /*!
661    * \brief Computes more 2 axes by one
662    *  \param [in] iOk - index of a given axis
663    *  \param [in,out] dirs - directions of 3 axes
664    */
665   //================================================================================
666
667   void get3Dirs( int iOk, gp_XYZ dirs[3] )
668   {
669     dirs[ ( iOk+1 ) % 3 ] = dirs[ iOk ];
670
671     if ( Abs( dirs[ iOk ].Y() ) < 1e-100 &&
672          Abs( dirs[ iOk ].Z() ) < 1e-100 )
673       // dirs[ iOk ] || OX
674       dirs[ ( iOk+1 ) % 3 ].SetY( dirs[ iOk ].Y() + 1. );
675     else
676       dirs[ ( iOk+1 ) % 3 ].SetX( dirs[ iOk ].X() + 1. );
677
678     dirs[( iOk+2 ) % 3] = dirs[ iOk ] ^ dirs[ ( iOk+1 ) % 3 ];
679     dirs[( iOk+1 ) % 3] = dirs[ ( iOk+2 ) % 3 ] ^ dirs[ iOk ];
680   }
681
682   //================================================================================
683   /*!
684    * \brief Returns a minimal width of a SpinBox depending on a precision type
685    */
686   //================================================================================
687
688   int getMinWidth( const char* precisionType )
689   {
690     int nb = SMESHGUI::resourceMgr()->integerValue( "SMESH", precisionType, -3 );
691     QString s;
692     s.fill('0', qAbs(nb)+7 );
693     QLineEdit le;
694     QFontMetrics metrics( le.font() );
695     return metrics.width( s );
696   }
697 }
698
699 //================================================================================
700 /*!
701  * \brief StdMeshersGUI_CartesianParamCreator constructor
702  */
703 //================================================================================
704
705 StdMeshersGUI_CartesianParamCreator::StdMeshersGUI_CartesianParamCreator(const QString& aHypType)
706   : StdMeshersGUI_StdHypothesisCreator( aHypType ),
707     myThreshold( 0 )
708 {
709   myAxisTabs[0] = 0;
710   myAxisTabs[1] = 0;
711   myAxisTabs[2] = 0;
712
713   myAxesPreview = new SMESHGUI_MeshEditPreview( SMESH::GetViewWindow( SMESHGUI::GetSMESHGUI() ));
714   myAxesPreview->SetArrowShapeAndNb( /*nbArrows=*/3,
715                                      /*headLength=*/0.1,
716                                      /*headRadius=*/0.01,
717                                      /*start=*/0.,
718                                      /*labels=*/"XYZ");
719
720   myDirTic[0] = myDirTic[1] = myDirTic[2] = 0;
721 }
722
723 //================================================================================
724 /*!
725  * \brief StdMeshersGUI_CartesianParamCreator destructor
726  */
727 //================================================================================
728
729 StdMeshersGUI_CartesianParamCreator::~StdMeshersGUI_CartesianParamCreator()
730 {
731   if ( myAxisTabs[0] ) delete myAxisTabs[0];
732   if ( myAxisTabs[1] ) delete myAxisTabs[1];
733   if ( myAxisTabs[2] ) delete myAxisTabs[2];
734   myAxisTabs[0] = 0;
735   myAxisTabs[1] = 0;
736   myAxisTabs[2] = 0;
737
738   delete myAxesPreview;
739 }
740
741 //================================================================================
742 /*!
743  * \brief Validate parameters
744  */
745 //================================================================================
746
747 bool StdMeshersGUI_CartesianParamCreator::checkParams( QString& msg ) const
748 {
749   if( !SMESHGUI_GenericHypothesisCreator::checkParams( msg ) )
750     return false;
751
752   if ( myName && myName->text().trimmed().isEmpty() )
753   {
754     msg = tr("SMESH_WRN_EMPTY_NAME");
755     return false;
756   }
757   if ( ! myThreshold->isValid( msg, true ))
758     return false;
759
760   SMESH::SMESH_Hypothesis_var hyp = hypothesis();
761   if ( !myAxisTabs[0]->checkParams( msg, hyp )) return false;
762   if ( !myAxisTabs[1]->checkParams( msg, hyp )) return false;
763   if ( !myAxisTabs[2]->checkParams( msg, hyp )) return false;
764
765   StdMeshersGUI_CartesianParamCreator* me = (StdMeshersGUI_CartesianParamCreator*) this;
766   if ( !me->updateAxesPreview() )
767   {
768     msg = tr("INVALID_AXES_DIR");
769     return false;
770   }
771
772   return true;
773 }
774
775 //================================================================================
776 /*!
777  * \brief Create widgets
778  */
779 //================================================================================
780
781 QFrame* StdMeshersGUI_CartesianParamCreator::buildFrame()
782 {
783   QFrame* fr = new QFrame();
784   //fr->setMinimumWidth(460);
785
786   QVBoxLayout* lay = new QVBoxLayout( fr );
787   lay->setMargin( 0 );
788   lay->setSpacing( SPACING );
789
790   QGroupBox* GroupC1 = new QGroupBox( tr( "SMESH_ARGUMENTS" ), fr );
791   lay->addWidget( GroupC1 );
792
793   StdMeshers::StdMeshers_NumberOfSegments_var h =
794     StdMeshers::StdMeshers_NumberOfSegments::_narrow( hypothesis() );
795
796   QGridLayout* argGroupLayout = new QGridLayout( GroupC1 );
797   argGroupLayout->setSpacing( SPACING );
798   argGroupLayout->setMargin( MARGIN );
799   argGroupLayout->setColumnStretch( 0, 0 );
800   argGroupLayout->setColumnStretch( 1, 1 );
801
802   int row = 0;
803   // 0)  name
804   myName = 0;
805   if( isCreation() )
806   {
807     myName = new QLineEdit( GroupC1 );
808     argGroupLayout->addWidget( new QLabel( tr( "SMESH_NAME" ), GroupC1 ), row, 0 );
809     argGroupLayout->addWidget( myName, row, 1 );
810     row++;
811   }
812
813   // 1)  threshold
814   argGroupLayout->addWidget( new QLabel( tr( "THRESHOLD" ), GroupC1 ), row, 0 );
815   myThreshold = new SMESHGUI_SpinBox( GroupC1 );
816   myThreshold->setAcceptNames( false ); // No Notebook variables allowed
817   myThreshold->RangeStepAndValidator( 1.00001, 1e+10, 1., "length_precision" );
818   argGroupLayout->addWidget( myThreshold, row, 1 );
819   row++;
820   
821   // 2)  "Implement edges"
822   myAddEdges = new QCheckBox( tr("ADD_EDGES"), GroupC1 );
823   argGroupLayout->addWidget( myAddEdges, row, 0, 1, 2 );
824   row++;
825
826   // 3)  Grid definition
827   QTabWidget* tabWdg = new QTabWidget( fr );
828   myAxisTabs[ 0 ] = new StdMeshersGUI::GridAxisTab( tabWdg, 0 );
829   myAxisTabs[ 1 ] = new StdMeshersGUI::GridAxisTab( tabWdg, 1 );
830   myAxisTabs[ 2 ] = new StdMeshersGUI::GridAxisTab( tabWdg, 2 );
831   tabWdg->addTab( myAxisTabs[ 0 ], tr( "AXIS_X" ) );
832   tabWdg->addTab( myAxisTabs[ 1 ], tr( "AXIS_Y" ) );
833   tabWdg->addTab( myAxisTabs[ 2 ], tr( "AXIS_Z" ) );
834   argGroupLayout->addWidget( tabWdg, row, 0, 1, 2 );
835   row++;
836
837   QPixmap aPix = SMESHGUI::resourceMgr()->loadPixmap("SMESH", tr("ICON_SELECT"));
838
839   // 4) Fixed point
840   myFixedPointGrp = new QGroupBox( tr("FIXED_POINT"), fr );
841   myFixedPointGrp->setCheckable( true );
842   //QPushButton* pointBtn = new QPushButton( QIcon(aPix), "", myFixedPointGrp );
843   QLabel* pXLbl = new QLabel( tr("SMESH_X"), myFixedPointGrp );
844   QLabel* pYLbl = new QLabel( tr("SMESH_Y"), myFixedPointGrp );
845   QLabel* pZLbl = new QLabel( tr("SMESH_Z"), myFixedPointGrp );
846   for ( int i = 0; i < 3; ++i )
847   {
848     myPointSpin[i] = new SMESHGUI_SpinBox( myFixedPointGrp );
849     myPointSpin[i]->RangeStepAndValidator( -1e20, 1e20, 10 );
850     myPointSpin[i]->SetValue( 0. );
851   }
852   QHBoxLayout* aFixedPointLay = new QHBoxLayout( myFixedPointGrp );
853   aFixedPointLay->addWidget( pXLbl, 0, Qt::AlignRight );
854   aFixedPointLay->addWidget( myPointSpin[0], 1 );
855   aFixedPointLay->addWidget( pYLbl, 0, Qt::AlignRight );
856   aFixedPointLay->addWidget( myPointSpin[1], 1 );
857   aFixedPointLay->addWidget( pZLbl, 0, Qt::AlignRight );
858   aFixedPointLay->addWidget( myPointSpin[2], 1 );
859   argGroupLayout->addWidget( myFixedPointGrp, row, 0, 1, 2 );
860   row++;
861
862   // 5) Axes direction
863   QGroupBox* axesDirGrp = new QGroupBox( tr("AXES_DIRECTION"), fr );
864   QGridLayout* axisDirLay = new QGridLayout( axesDirGrp );
865   axisDirLay->setSpacing( SPACING );
866   axisDirLay->setMargin( MARGIN );
867   axisDirLay->setColumnStretch( 0, 2 );
868   // is orthogonal
869   myOrthogonalChk = new QCheckBox( tr("ORTHOGONAL_AXES"), axesDirGrp );
870   axisDirLay->addWidget( myOrthogonalChk, 0, 0, 1, 7 );
871   // axes
872   QLabel* axisLbl[3];
873   axisLbl[0] = new QLabel( tr( "AXIS_X"), axesDirGrp );
874   axisLbl[1] = new QLabel( tr( "AXIS_Y"), axesDirGrp );
875   axisLbl[2] = new QLabel( tr( "AXIS_Z"), axesDirGrp );
876   QLabel* dLbl[3];
877   myAxisBtnGrp = new QButtonGroup( axesDirGrp );
878   // get spin width
879   const char * const precisionType = "len_tol_precision";
880   int minWidth = getMinWidth( precisionType );
881   for ( int i = 0; i < 3; ++i )
882   {
883     QPushButton* axisBtn = new QPushButton( QIcon(aPix), "", axesDirGrp );
884     axisBtn->setCheckable( true );
885     myAxisBtnGrp->addButton( axisBtn, i );
886     myXDirSpin[i] = new SMESHGUI_SpinBox( axesDirGrp );
887     myYDirSpin[i] = new SMESHGUI_SpinBox( axesDirGrp );
888     myZDirSpin[i] = new SMESHGUI_SpinBox( axesDirGrp );
889     myXDirSpin[i]->RangeStepAndValidator( -1, 1, 0.1, precisionType );
890     myYDirSpin[i]->RangeStepAndValidator( -1, 1, 0.1, precisionType );
891     myZDirSpin[i]->RangeStepAndValidator( -1, 1, 0.1, precisionType );
892     myXDirSpin[i]->setMinimumWidth( minWidth );
893     myYDirSpin[i]->setMinimumWidth( minWidth );
894     myZDirSpin[i]->setMinimumWidth( minWidth );
895     dLbl[0] = new QLabel( tr("SMESH_DX"), axesDirGrp );
896     dLbl[1] = new QLabel( tr("SMESH_DY"), axesDirGrp );
897     dLbl[2] = new QLabel( tr("SMESH_DZ"), axesDirGrp );
898     axisDirLay->addWidget( axisLbl[i],    i+1, 0 );
899     axisDirLay->addWidget( axisBtn,       i+1, 1 );
900     axisDirLay->addWidget( dLbl[0],       i+1, 2 );
901     axisDirLay->addWidget( dLbl[1],       i+1, 4 );
902     axisDirLay->addWidget( dLbl[2],       i+1, 6 );
903     axisDirLay->addWidget( myXDirSpin[i], 1, 3+i*2 );
904     axisDirLay->addWidget( myYDirSpin[i], 2, 3+i*2 );
905     axisDirLay->addWidget( myZDirSpin[i], 3, 3+i*2 );
906   }
907   axisDirLay->setColumnStretch( 3, 10 );
908   axisDirLay->setColumnStretch( 5, 10 );
909   axisDirLay->setColumnStretch( 7, 10 );
910
911   // set optimal axes
912   QPushButton* optimBtn = new QPushButton( tr("OPTIMAL_AXES"), axesDirGrp );
913   QPushButton* resetBtn = new QPushButton( tr("RESET_AXES"), axesDirGrp );
914   axisDirLay->addWidget( optimBtn, 4, 0, 1, 4 );
915   axisDirLay->addWidget( resetBtn, 4, 4, 1, 4 );
916
917   argGroupLayout->addWidget( axesDirGrp, row, 0, 1, 2 );
918   row++;
919
920   // Signals
921
922   LightApp_SelectionMgr* selMgr = SMESH::GetSelectionMgr( SMESHGUI::GetSMESHGUI() );
923
924   connect( selMgr,          SIGNAL( currentSelectionChanged()), SLOT( onSelectionChange()));
925   connect( myOrthogonalChk, SIGNAL( toggled(bool)),             SLOT( onOrthogonalAxes(bool)));
926   connect( optimBtn,        SIGNAL( clicked(bool)),             SLOT( onOptimalAxes(bool)));
927   connect( resetBtn,        SIGNAL( clicked(bool)),             SLOT( onResetAxes(bool)));
928   for ( int i = 0; i < 3; ++i )
929   {
930     connect( myXDirSpin[i], SIGNAL(valueChanged   (const QString&)),
931              this,          SLOT  (onAxisDirChange(const QString&)) );
932     connect( myYDirSpin[i], SIGNAL(valueChanged   (const QString&)),
933              this,          SLOT  (onAxisDirChange(const QString&)) );
934     connect( myZDirSpin[i], SIGNAL(valueChanged   (const QString&)),
935              this,          SLOT  (onAxisDirChange(const QString&)) );
936     connect( myAxisTabs[i], SIGNAL(gridModeChanged(int)),
937              this,          SLOT  (onGridModeChanged(int)));
938   }
939
940   // Show axes
941   myAxesLen = 120; // default trihedron size is 100
942   myOrigin[0] = myOrigin[1] = myOrigin[2] = 0.;
943   TopoDS_Shape shape;
944   QString shapeEntry = getMainShapeEntry();
945   if ( !shapeEntry.isEmpty() )
946   {
947     // find origin
948     Handle(SALOME_InteractiveObject) io =
949       new SALOME_InteractiveObject( shapeEntry.toStdString().c_str(), "GEOM" );
950     GEOM::GEOM_Object_var geomObj = SMESH::IObjectToInterface<GEOM::GEOM_Object>( io );
951     if ( GEOMBase::GetShape( geomObj, shape ) && !shape.IsNull())
952     {
953       Bnd_Box box;
954       BRepBndLib::Add( shape, box );
955       double max[3];
956       if ( !box.IsVoid() )
957       {
958         box.Get( myOrigin[0], myOrigin[1], myOrigin[2], max[0], max[1], max[2] );
959         gp_Pnt o( myOrigin[0], myOrigin[1], myOrigin[2] );
960         gp_Pnt x( max[0], max[1], max[2] );
961         myAxesLen = o.Distance( x );
962
963         double step = 1e20;
964         while ( step > myAxesLen / 5 )
965           step /= 10;
966         myPointSpin[0]->SetStep( step );
967         myPointSpin[1]->SetStep( step );
968         myPointSpin[2]->SetStep( step );
969       }
970     }
971   }
972   myAxisBtnGrp->button(0)->setEnabled( !shape.IsNull() );
973   myAxisBtnGrp->button(1)->setEnabled( !shape.IsNull() );
974   myAxisBtnGrp->button(2)->setEnabled( !shape.IsNull() );
975   optimBtn->setEnabled( !shape.IsNull() );
976
977   updateAxesPreview();
978
979   return fr;
980 }
981
982 //================================================================================
983 /*!
984  * \brief Tranfer parameters from hypothesis to widgets
985  */
986 //================================================================================
987
988 void StdMeshersGUI_CartesianParamCreator::retrieveParams() const
989 {
990   StdMeshers::StdMeshers_CartesianParameters3D_var h =
991     StdMeshers::StdMeshers_CartesianParameters3D::_narrow( initParamsHypothesis() );
992
993   if( myName )
994     myName->setText( hypName() );
995
996   QString varName = getVariableName( "SetSizeThreshold" );
997   if ( varName.isEmpty() )
998     myThreshold->setValue( h->GetSizeThreshold() );
999   else
1000     myThreshold->setText( varName );
1001
1002   myAddEdges->setChecked( h->GetToAddEdges() );
1003
1004   // grid definition
1005   for ( int ax = 0; ax < 3; ++ax )
1006   {
1007     if ( h->IsGridBySpacing( ax ))
1008     {
1009       SMESH::string_array_var funs;
1010       SMESH::double_array_var intPoints;
1011       h->GetGridSpacing( funs.out(), intPoints.out(), ax );
1012       myAxisTabs[ax]->setSpacing( funs, intPoints );
1013     }
1014     else
1015     {
1016       SMESH::double_array_var coords = h->GetGrid( ax );
1017       myAxisTabs[ax]->setCoordinates( coords );
1018     }
1019   }
1020
1021   // fixed point
1022   SMESH::PointStruct fp;
1023   StdMeshersGUI_CartesianParamCreator* me = (StdMeshersGUI_CartesianParamCreator*) this;
1024   if ( h->GetFixedPoint( fp ))
1025   {
1026     me->myPointSpin[0]->SetValue( fp.x );
1027     me->myPointSpin[1]->SetValue( fp.y );
1028     me->myPointSpin[2]->SetValue( fp.z );
1029     setText( getVariableName("GetFixedPoint"), &me->myPointSpin[0] );
1030     myFixedPointGrp->setChecked( true );
1031   }
1032   else
1033   {
1034     myFixedPointGrp->setChecked( false );
1035   }
1036
1037   // axes directions
1038   SMESHGUI_SpinBox** spins[3] = { &me->myXDirSpin[0], &me->myYDirSpin[0], &me->myZDirSpin[0] };
1039   SMESH::DirStruct axisDir[3];
1040   h->GetAxesDirs( axisDir[0],
1041                   axisDir[1],
1042                   axisDir[2]);
1043   QString vars = getVariableName("GetAxesDirs");
1044   for ( int i = 0; i < 3; ++i )
1045   {
1046     spins[i][0]->SetValue( axisDir[i].PS.x );
1047     spins[i][1]->SetValue( axisDir[i].PS.y );
1048     spins[i][2]->SetValue( axisDir[i].PS.z );
1049     setText( vars, spins[i] );
1050
1051     // cut off 3 used vars
1052     if ( !vars.isEmpty() )
1053     {
1054       int ind = -1;
1055       for ( int j = 0; j < 3; ++j )
1056         if (( ind = vars.indexOf(':', ind+1 )) < 0 )
1057           break;
1058       if ( ind < 0 )
1059         vars.clear();
1060       else
1061         vars.remove( 0, ind+1 );
1062     }
1063   }
1064
1065   if ( dlg() )
1066     dlg()->setMinimumSize( dlg()->minimumSizeHint().width(),
1067                            dlg()->minimumSizeHint().height() );
1068 }
1069
1070 //================================================================================
1071 /*!
1072  * \brief Tranfer parameters from widgets to hypothesis
1073  */
1074 //================================================================================
1075
1076 QString StdMeshersGUI_CartesianParamCreator::storeParams() const
1077 {
1078   StdMeshers::StdMeshers_CartesianParameters3D_var h =
1079     StdMeshers::StdMeshers_CartesianParameters3D::_narrow( hypothesis() );
1080
1081   try
1082   {
1083     if( isCreation() )
1084       SMESH::SetName( SMESH::FindSObject( h ), myName->text().toLatin1().constData() );
1085
1086     // threshold
1087     h->SetVarParameter( myThreshold->text().toLatin1().constData(), "SetSizeThreshold" );
1088     h->SetSizeThreshold( myThreshold->text().toDouble() );
1089     h->SetToAddEdges( myAddEdges->isChecked() );
1090
1091     // grid
1092     for ( int ax = 0; ax < 3; ++ax )
1093     {
1094       if ( myAxisTabs[ax]->isGridBySpacing())
1095       {
1096         SMESH::double_array_var intPoints;
1097         SMESH::string_array_var funs;
1098         myAxisTabs[ax]->getSpacing( funs.out(), intPoints.out() );
1099         h->SetGridSpacing( funs, intPoints, ax );
1100       }
1101       else
1102       {
1103         SMESH::double_array_var coords = myAxisTabs[ax]->getCoordinates();
1104         h->SetGrid( coords, ax );
1105       }
1106     }
1107
1108     // fixed point
1109     QStringList params;
1110     params << myPointSpin[0]->text();
1111     params << myPointSpin[1]->text();
1112     params << myPointSpin[2]->text();
1113     h->SetVarParameter( params.join(":").toLatin1().constData(), "SetFixedPoint" );
1114     params.clear();
1115
1116     SMESH::PointStruct ps;
1117     ps.x = myPointSpin[0]->GetValue();
1118     ps.y = myPointSpin[1]->GetValue();
1119     ps.z = myPointSpin[2]->GetValue();
1120     h->SetFixedPoint( ps, !myFixedPointGrp->isEnabled() || !myFixedPointGrp->isChecked() );
1121
1122     // axes directions
1123     SMESHGUI_SpinBox* const * spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
1124     for ( int ax = 0; ax < 3; ++ax )
1125     {
1126       params << spins[ax][0]->text();
1127       params << spins[ax][1]->text();
1128       params << spins[ax][2]->text();
1129     }
1130     h->SetVarParameter( params.join(":").toLatin1().constData(), "SetAxesDirs" );
1131
1132     SMESH::DirStruct axDir[3];
1133     for ( int ax = 0; ax < 3; ++ax )
1134     {
1135       axDir[ax].PS.x = spins[ax][0]->GetValue();
1136       axDir[ax].PS.y = spins[ax][1]->GetValue();
1137       axDir[ax].PS.z = spins[ax][2]->GetValue();
1138     }
1139     h->SetAxesDirs( axDir[0], axDir[1], axDir[2] );
1140
1141   }
1142   catch(const SALOME::SALOME_Exception& ex)
1143   {
1144     SalomeApp_Tools::QtCatchCorbaException(ex);
1145   }
1146   return "";
1147 }
1148
1149 //================================================================================
1150 /*!
1151  * \brief Returns a name of help page
1152  */
1153 //================================================================================
1154
1155 QString StdMeshersGUI_CartesianParamCreator::helpPage() const
1156 {
1157   return "cartesian_algo_page.html#cartesian_hyp_anchor";
1158 }
1159
1160 //================================================================================
1161 /*!
1162  * \brief Show axes if they are OK
1163  */
1164 //================================================================================
1165
1166 bool StdMeshersGUI_CartesianParamCreator::updateAxesPreview()
1167 {
1168   bool isOk = true;
1169   gp_Ax1 axes[3];
1170   SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
1171   for ( int i = 0; i < 3 && isOk; ++i )
1172   {
1173     gp_XYZ dir( spins[i][0]->GetValue(),
1174                 spins[i][1]->GetValue(),
1175                 spins[i][2]->GetValue());
1176     if (( isOk = ( dir.Modulus() > 1e-100 )))
1177       axes[i].SetDirection( gp_Dir( dir ));
1178
1179     axes[i].SetLocation ( gp_Pnt( myOrigin[0],
1180                                   myOrigin[1],
1181                                   myOrigin[2]));
1182   }
1183   gp_Vec norm01 = axes[0].Direction().XYZ() ^ axes[1].Direction().XYZ();
1184   gp_Vec norm12 = axes[1].Direction().XYZ() ^ axes[2].Direction().XYZ();
1185   if ( isOk )
1186     isOk = ( !axes[0].Direction().IsParallel( axes[1].Direction(), theAngTol ) &&
1187              !axes[1].Direction().IsParallel( axes[2].Direction(), theAngTol ) &&
1188              !axes[2].Direction().IsParallel( axes[0].Direction(), theAngTol ) &&
1189              !norm01.IsParallel( norm12, theAngTol ) );
1190   if ( isOk )
1191     myAxesPreview->SetArrows( axes, myAxesLen );
1192
1193   myAxesPreview->SetVisibility( isOk );
1194
1195   return isOk;
1196 }
1197
1198 //================================================================================
1199 /*!
1200  * \brief Makes axes orthogonal if necessary
1201  */
1202 //================================================================================
1203
1204 void StdMeshersGUI_CartesianParamCreator::onOrthogonalAxes(bool isOrtho)
1205 {
1206   if ( !isOrtho )
1207   {
1208     updateAxesPreview();
1209     return;
1210   }
1211
1212   std::multimap< int, int > ageOfAxis;
1213   gp_XYZ dirs[3];
1214   SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
1215   int nbOk = 0, isOk;
1216   for ( int iAx = 0; iAx < 3; ++iAx )
1217   {
1218     dirs[iAx].SetCoord( spins[iAx][0]->GetValue(),
1219                         spins[iAx][1]->GetValue(),
1220                         spins[iAx][2]->GetValue());
1221     if (( isOk = ( dirs[iAx].Modulus() > 1e-100 )))
1222       ageOfAxis.insert( std::make_pair( myDirTic[iAx], iAx ));
1223     else
1224       ageOfAxis.insert( std::make_pair( -1, iAx ));
1225     nbOk += isOk;
1226   }
1227   switch ( nbOk )
1228   {
1229   case 0:
1230   {
1231     dirs[0].SetCoord( 1, 0, 0 );
1232     dirs[1].SetCoord( 0, 1, 0 );
1233     dirs[2].SetCoord( 0, 0, 1 );
1234     break;
1235   }
1236   case 1:
1237   {
1238     int iOk = ageOfAxis.rbegin()->second;
1239     get3Dirs( iOk, dirs );
1240     break;
1241   }
1242   default:
1243     std::multimap< int, int >::reverse_iterator ag2ax = ageOfAxis.rbegin();
1244     int iOk1 = ag2ax->second;
1245     int iOk2 = (++ag2ax)->second;
1246     int iKo  = (++ag2ax)->second;
1247     if ( gp_Vec( dirs[ iOk1 ]).IsParallel( gp_Vec( dirs[ iOk2 ]), theAngTol ))
1248       std::swap( iOk2, iKo );
1249     if ( gp_Vec( dirs[ iOk1 ]).IsParallel( gp_Vec( dirs[ iOk2 ]), theAngTol ))
1250     {
1251       get3Dirs( iOk1, dirs );
1252     }
1253     else
1254     {
1255       dirs[ iKo  ] = dirs[ iOk1 ] ^ dirs[ iOk2 ];
1256       dirs[ iOk2 ] = dirs[ iKo  ] ^ dirs[ iOk1 ];
1257       if ( ( iOk1+1 ) % 3 != iOk2 )
1258         dirs[ iKo ].Reverse();
1259     }
1260   }
1261
1262   for ( int iAx = 0; iAx < 3; ++iAx )
1263   {
1264     double size = dirs[iAx].Modulus();
1265     if ( size > 1e-100 )
1266       dirs[iAx] /= size;
1267     for (int i = 0; i < 3; ++i )
1268     {
1269       bool isBlocked = spins[iAx][i]->blockSignals( true );
1270       spins[iAx][i]->SetValue( dirs[iAx].Coord( i+1 ));
1271       spins[iAx][i]->blockSignals( isBlocked );
1272     }
1273   }
1274
1275   updateAxesPreview();
1276 }
1277
1278 //================================================================================
1279 /*!
1280  * \brief Increment myDirTic and update the preview of axes
1281  */
1282 //================================================================================
1283
1284 void StdMeshersGUI_CartesianParamCreator::onAxisDirChange(const QString&)
1285 {
1286   QObject* changedSpin = sender();
1287   SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
1288   for ( int iAx = 0; iAx < 3; ++iAx )
1289     if ( spins[iAx][0] == changedSpin ||
1290          spins[iAx][1] == changedSpin ||
1291          spins[iAx][2] == changedSpin )
1292     {
1293       myDirTic[ iAx ] = 1 + Max( Max( myDirTic[0], myDirTic[1] ), myDirTic[2] );
1294       break;
1295     }
1296
1297   onOrthogonalAxes( myOrthogonalChk->isChecked() );
1298 }
1299
1300 //================================================================================
1301 /*!
1302  * \brief Sets axis direction by a selected EDGE
1303  */
1304 //================================================================================
1305
1306 void StdMeshersGUI_CartesianParamCreator::onSelectionChange()
1307 {
1308   int iAxis = myAxisBtnGrp->checkedId();
1309   if ( iAxis < 0 )
1310     return;
1311
1312   SALOME_ListIO aList;
1313   SMESHGUI::GetSMESHGUI()->selectionMgr()->selectedObjects(aList);
1314
1315   TopoDS_Shape edge, shape;
1316   for( SALOME_ListIteratorOfListIO anIt( aList ); anIt.More(); anIt.Next() )
1317   {
1318     GEOM::GEOM_Object_var go = SMESH::IObjectToInterface<GEOM::GEOM_Object>( anIt.Value() );
1319     if ( GEOMBase::GetShape( go, shape ) && shape.ShapeType() == TopAbs_EDGE )
1320     {
1321       if ( !edge.IsNull() )
1322         return; // several EDGEs selected
1323       edge = shape;
1324     }
1325   }
1326   if ( edge.IsNull() )
1327     return;
1328
1329   TopoDS_Shape vv[2];
1330   TopoDS_Iterator vIt( edge );
1331   for ( ; vIt.More() && vv[1].IsNull(); vIt.Next() )
1332     vv[ !vv[0].IsNull() ] = vIt.Value();
1333
1334   gp_Pnt pp[2];
1335   if ( !GEOMBase::VertexToPoint( vv[0], pp[0] ) ||
1336        !GEOMBase::VertexToPoint( vv[1], pp[1] ))
1337     return;
1338
1339   SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
1340
1341   gp_Vec newDir( pp[0], pp[1] );
1342   gp_Vec curDir( spins[iAxis][0]->GetValue(),
1343                  spins[iAxis][1]->GetValue(),
1344                  spins[iAxis][2]->GetValue());
1345   if ( newDir * curDir < 0 )
1346     newDir.Reverse();
1347
1348   double size = newDir.Magnitude();
1349   if ( size < 1e-100 )
1350     return;
1351   newDir /= size;
1352
1353   for (int i = 0; i < 3; ++i )
1354   {
1355     bool isBlocked = spins[iAxis][i]->blockSignals( true );
1356     spins[iAxis][i]->SetValue( newDir.Coord( i+1 ));
1357     spins[iAxis][i]->blockSignals( isBlocked );
1358   }
1359   myDirTic[ iAxis ] = 1 + Max( Max( myDirTic[0], myDirTic[1] ), myDirTic[2] );
1360
1361   onOrthogonalAxes( myOrthogonalChk->isChecked() );
1362 }
1363
1364 //================================================================================
1365 /*!
1366  * \brief Sets axes at which number of hexahedra is maximal
1367  */
1368 //================================================================================
1369
1370 void StdMeshersGUI_CartesianParamCreator::onOptimalAxes(bool)
1371 {
1372   StdMeshers::StdMeshers_CartesianParameters3D_var h =
1373     StdMeshers::StdMeshers_CartesianParameters3D::_narrow( hypothesis() );
1374   if ( h->_is_nil() )
1375     return;
1376
1377   QString shapeEntry = getMainShapeEntry();
1378   if ( shapeEntry.isEmpty() )
1379     return;
1380
1381   Handle(SALOME_InteractiveObject) io =
1382     new SALOME_InteractiveObject( shapeEntry.toStdString().c_str(), "GEOM" );
1383   GEOM::GEOM_Object_var geomObj = SMESH::IObjectToInterface<GEOM::GEOM_Object>( io );
1384   if ( geomObj->_is_nil() )
1385     return;
1386
1387   SMESH::DirStruct axDirs[3];
1388   h->ComputeOptimalAxesDirs( geomObj,
1389                              myOrthogonalChk->isChecked(),
1390                              axDirs[0],
1391                              axDirs[1],
1392                              axDirs[2]);
1393
1394   SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
1395   for ( int iAx = 0; iAx < 3; ++iAx )
1396   {
1397     double coords[3] = { axDirs[iAx].PS.x, axDirs[iAx].PS.y, axDirs[iAx].PS.z };
1398     for (int i = 0; i < 3; ++i )
1399     {
1400       bool isBlocked = spins[iAx][i]->blockSignals( true );
1401       spins[iAx][i]->SetValue( coords[ i ]);
1402       spins[iAx][i]->blockSignals( isBlocked );
1403     }
1404   }
1405   updateAxesPreview();
1406 }
1407
1408 //================================================================================
1409 /*!
1410  * \brief Sets axes || to the axes of global CS
1411  */
1412 //================================================================================
1413
1414 void StdMeshersGUI_CartesianParamCreator::onResetAxes(bool)
1415 {
1416   SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
1417   for ( int iAx = 0; iAx < 3; ++iAx )
1418   {
1419     for (int i = 0; i < 3; ++i )
1420     {
1421       bool isBlocked = spins[iAx][i]->blockSignals( true );
1422       spins[iAx][i]->SetValue( iAx == i ? 1. : 0. );
1423       spins[iAx][i]->blockSignals( isBlocked );
1424     }
1425     myDirTic[iAx] = 0;
1426   }
1427   updateAxesPreview();
1428 }
1429
1430 //================================================================================
1431 /*!
1432  * \brief SLOT called when the grid definintion mode changes
1433  */
1434 //================================================================================
1435
1436 void StdMeshersGUI_CartesianParamCreator::onGridModeChanged(int)
1437 {
1438   bool haveSpacing = ( myAxisTabs[0]->isGridBySpacing() ||
1439                        myAxisTabs[1]->isGridBySpacing() ||
1440                        myAxisTabs[2]->isGridBySpacing() );
1441
1442   myFixedPointGrp->setEnabled( haveSpacing );
1443 }