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