Salome HOME
3369aaa95f133114d16b939ba94e5aa9cda6e2b5
[modules/smesh.git] / src / StdMeshersGUI / StdMeshersGUI_PropagationHelperWdg.cxx
1 // Copyright (C) 2007-2015  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // File      : StdMeshersGUI_PropagationHelperWdg.cxx
20 // Created   : Thu Mar 19 18:46:24 2015
21 // Author    : Edward AGAPOV (eap)
22
23 #include "StdMeshersGUI_PropagationHelperWdg.h"
24
25 #include "StdMeshersGUI_SubShapeSelectorWdg.h"
26 #include "SMESH_PreviewActorsCollection.h"
27 #include "SMESHGUI_VTKUtils.h"
28
29 #include <GEOM_Actor.h>
30
31 #include <LightApp_SelectionMgr.h>
32 #include <SUIT_OverrideCursor.h>
33 #include <SVTK_ViewWindow.h>
34 #include <vtkRenderer.h>
35
36 #include <QCheckBox>
37 #include <QGridLayout>
38 #include <QGroupBox>
39 #include <QLabel>
40 #include <QList>
41 #include <QListWidget>
42 #include <QListWidgetItem>
43 #include <QPushButton>
44
45 #include <BRepTools_WireExplorer.hxx>
46 #include <BRep_Builder.hxx>
47 #include <NCollection_DataMap.hxx>
48 #include <TColStd_IndexedMapOfInteger.hxx>
49 #include <TopExp_Explorer.hxx>
50 #include <TopoDS.hxx>
51 #include <TopoDS_Compound.hxx>
52 #include <TopoDS_Iterator.hxx>
53
54 #include <set>
55
56 #define SPACING 6
57 #define MARGIN 11
58
59 //================================================================================
60 /*!
61  * \brief Constructor
62  */
63 //================================================================================
64
65 StdMeshersGUI_PropagationHelperWdg::
66 StdMeshersGUI_PropagationHelperWdg( StdMeshersGUI_SubShapeSelectorWdg* subSelectWdg,
67                                     QWidget*                           parent,
68                                     bool                               show ):
69   QWidget( parent ), mySubSelectWdg( subSelectWdg ), myActor( 0 ), myModelActor( 0 )
70 {
71   QGroupBox* helperBox = new QGroupBox( tr("HELPER"), this );
72   myShowGeomChkBox     = new QCheckBox( tr("SHOW_GEOMETRY"), helperBox );
73   myChainBox           = new QGroupBox( tr("PROPAGATION_CHAINS"), helperBox );
74   myChainBox->setCheckable( true );
75   myChainBox->setChecked( false );
76   myListWidget         = new QListWidget( helperBox );
77   myListWidget->setSelectionMode( QAbstractItemView::SingleSelection );
78   myAddButton          = new QPushButton( tr("ADD"), helperBox );
79   myReverseButton      = new QPushButton( tr("REVERSE"), helperBox );
80
81   QGridLayout* chainsLayout = new QGridLayout( myChainBox );
82   chainsLayout->setMargin( MARGIN );
83   chainsLayout->setSpacing( SPACING );
84   chainsLayout->addWidget(myListWidget,    0, 0, 3, 3);
85   chainsLayout->addWidget(myAddButton,     0, 3);
86   chainsLayout->addWidget(myReverseButton, 1, 3);
87
88   QVBoxLayout* helperLayout = new QVBoxLayout( helperBox );
89   helperLayout->setMargin( MARGIN );
90   helperLayout->setSpacing( SPACING );
91   helperLayout->addWidget( myShowGeomChkBox );
92   helperLayout->addWidget( myChainBox );
93
94   QVBoxLayout* lay = new QVBoxLayout( this );
95   lay->setMargin( 0 );
96   lay->setSpacing( SPACING );
97   lay->addWidget( helperBox );
98
99   connect( myShowGeomChkBox,SIGNAL( toggled(bool)), SLOT( onShowGeometry(bool)));
100   connect( myChainBox,        SIGNAL( toggled(bool)), SLOT( updateList(bool)));
101   connect( myListWidget,    SIGNAL( itemSelectionChanged()), SLOT( onListSelectionChanged() ));
102   connect( myAddButton,     SIGNAL( clicked(bool)), SLOT( onAdd() ));
103   connect( myReverseButton, SIGNAL( clicked(bool)), SLOT( onReverse() ));
104
105   if ( show )
106     onListSelectionChanged();
107 }
108
109 //================================================================================
110 /*!
111  * \brief Destructor
112  */
113 //================================================================================
114
115 StdMeshersGUI_PropagationHelperWdg::~StdMeshersGUI_PropagationHelperWdg()
116 {
117   if ( myActor )
118   {
119     myActor->RemoveFromRender( myRenderer );
120     myActor->Delete();
121     myActor = 0;
122   }
123   if ( myModelActor )
124   {
125     myModelActor->RemoveFromRender( myRenderer );
126     myModelActor->Delete();
127     myModelActor = 0;
128   }
129 }
130
131 //================================================================================
132 /*!
133  * \brief Switch off all buttons and previews
134  */
135 //================================================================================
136
137 void StdMeshersGUI_PropagationHelperWdg::Clear()
138 {
139   myShowGeomChkBox->setChecked( false );
140
141   myListWidget->blockSignals( true );
142   myListWidget->clear();
143   myListWidget->blockSignals( false );
144
145   myChainBox->blockSignals( true );
146   myChainBox->setChecked( false );
147   myChainBox->blockSignals( false );
148
149   if ( myActor )
150     myActor->SetVisibility( false );
151
152   if ( myModelActor )
153     myModelActor->SetVisibility( false );
154 }
155
156 //================================================================================
157 /*!
158  * \brief SLOT called when 'Show Geometry' is checked
159  */
160 //================================================================================
161
162 void StdMeshersGUI_PropagationHelperWdg::onShowGeometry(bool toShow)
163 {
164   if ( ! myModelActor )
165   {
166     TopoDS_Shape shape     = mySubSelectWdg->GetGeomShape();
167     TopoDS_Shape mainShape = mySubSelectWdg->GetMainShape();
168     if ( shape.IsNull() && mainShape.IsNull() ) return;
169     if ( mainShape.IsNull() ) mainShape = shape;
170
171     SUIT_OverrideCursor wc;
172
173     TopoDS_Compound aCompound;
174     BRep_Builder aBuilder;
175     aBuilder.MakeCompound( aCompound );
176     
177     SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
178     if ( !previewActor ) return;
179     const QList<int>& egdeIDs = previewActor->GetIndices();
180     for ( QList<int>::const_iterator ieIt = egdeIDs.begin(); ieIt != egdeIDs.end(); ++ieIt )
181     {
182       TopoDS_Shape E = previewActor->GetShapeByIndex( *ieIt );
183       if ( !E.IsNull() && E.ShapeType() == TopAbs_EDGE )
184         aBuilder.Add( aCompound, E );
185     }
186
187     myModelActor = GEOM_Actor::New();
188     myModelActor->SetShape( aCompound, 0, 0 );
189     myModelActor->SetPickable( false );
190     myModelActor->SetIsolatedEdgeColor( 0.5, 0.5, 0.5 );
191
192     if (( myRenderer = mySubSelectWdg->GetRenderer() ))
193       myModelActor->AddToRender( myRenderer );
194   }
195
196   if ( myModelActor )
197     myModelActor->SetVisibility( toShow );
198
199   SMESH::RepaintCurrentView();
200 }
201
202 //================================================================================
203 /*!
204  * \brief Build propagation chains. Return false if no chains found
205  */
206 //================================================================================
207
208 bool StdMeshersGUI_PropagationHelperWdg::buildChains()
209 {
210   if ( !myChains.empty() ) return false;
211
212   if ( !mySubSelectWdg ) return false;
213
214   TopoDS_Shape shape     = mySubSelectWdg->GetGeomShape();
215   TopoDS_Shape mainShape = mySubSelectWdg->GetMainShape();
216   if ( shape.IsNull() && mainShape.IsNull() ) return false;
217
218   if ( shape.IsNull() )     shape = mainShape;
219   if ( mainShape.IsNull() ) mainShape = shape;
220
221   SUIT_OverrideCursor wc;
222
223   // aPreviewActor holds a map od all sub-shapes of mainShape
224   SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
225   if ( !previewActor ) return false;
226   const QList<int>& egdeIDs = previewActor->GetIndices();
227
228   // Make a 'map' of WIREs of EDGE with quadrilateral WIREs only
229
230   typedef int                        TGeomID; // index in the mainShape
231   typedef std::vector< TGeomID >     TWire; // signed IDs of EDGEs, sign means orientation
232   typedef std::pair< int, TWire* >   TEdgeInWire; // index in TWire + TWire*
233   typedef std::vector< TEdgeInWire > TWiresOfEdge;// WIREs including an EDGE
234
235   std::vector< TWire > quadWires;
236   quadWires.reserve( egdeIDs.count() );
237   NCollection_DataMap< TGeomID, TWiresOfEdge > wiresOfEdge( egdeIDs.count() );
238
239   TopExp_Explorer wire;
240   TopTools_MapOfShape faceMap;
241   for ( TopExp_Explorer face( shape, TopAbs_FACE ); face.More(); face.Next() )
242   {
243     if ( !faceMap.Add( face.Current() )) continue;
244
245     wire.Init( face.Current(), TopAbs_WIRE );
246     TopoDS_Shape W = wire.Current().Oriented( TopAbs_FORWARD );
247
248     wire.Next();
249     if ( wire.More() ) continue;
250
251     // count EDGEs
252     int nbE = 0;
253     for ( TopoDS_Iterator edge( W, false, false ); edge.More() && nbE < 5; ++nbE )
254       edge.Next();
255     if ( nbE != 4 ) continue;
256
257     // fill a TWire and TWiresOfEdge
258     quadWires.push_back( TWire() );
259     TWire& wire = quadWires.back();
260     wire.reserve( 4 );
261     for ( BRepTools_WireExplorer edge( TopoDS::Wire( W )); edge.More(); edge.Next() )
262     {
263       const TopoDS_Shape& E = edge.Current();
264       int iE = previewActor->GetIndexByShape( E );
265       if ( iE < 1 )
266         continue;
267       if ( !wiresOfEdge.IsBound( iE ))
268         wiresOfEdge.Bind( iE, TWiresOfEdge() );
269       wiresOfEdge( iE ).push_back( TEdgeInWire( wire.size(), & wire ));
270
271       wire.push_back( E.Orientation() == TopAbs_REVERSED ? -iE : iE );
272     }
273   }
274   if ( quadWires.empty() )
275     return false;
276
277   // Find chains
278
279   TColStd_IndexedMapOfInteger chain, chainedEdges;
280
281   // loop on all EDGEs in mainShape
282   for ( QList<TGeomID>::const_iterator ieIt = egdeIDs.begin(); ieIt != egdeIDs.end(); ++ieIt )
283   {
284     if ( chainedEdges.Contains( *ieIt ))
285       continue;
286     // start a new chain
287     chain.Clear();
288     chain.Add( *ieIt );
289     chainedEdges.Add( *ieIt );
290     for ( int iC = 1; iC <= chain.Extent(); ++iC ) // loop on EDGE's in chain
291     {
292       TGeomID iE = chain( iC ), iEAbs = Abs( iE );
293       if ( !wiresOfEdge.IsBound( iEAbs ))
294         continue;
295       const TWiresOfEdge& wires = wiresOfEdge( iEAbs );
296       for (size_t i = 0; i < wires.size(); ++i ) // loop on WIREs sharing iE
297       {
298         const TEdgeInWire& eInW = wires[i];
299         const TWire&    W = *eInW.second;
300         if ( W.size() != 4 ) continue;
301         const int    iInW = eInW.first;
302         const int iInWOpp = ( iInW + 2 ) % 4; // an opposite edge index
303         TGeomID  iEOppAbs = Abs( W[ iInWOpp ] );
304
305         int prevNbChained = chainedEdges.Extent();
306         if ( prevNbChained < chainedEdges.Add( iEOppAbs ))
307         {
308           int dir = iE / iEAbs;
309           bool isSameDir = ( W[ iInW ] * W[ iInWOpp ] < 0 );
310           if ( !isSameDir )
311             dir *= -1;
312           chain.Add( dir * iEOppAbs );
313         }
314       }
315     }
316     // store a chain
317     if ( chain.Extent() > 1 )
318     {
319       myChains.push_back( std::vector< TGeomID >() );
320       std::vector< TGeomID > & ch = myChains.back();
321       for ( int iC = 1; iC <= chain.Extent(); ++iC )
322         ch.push_back( chain( iC ) );
323     }
324   }
325   return !myChains.empty();
326 }
327
328 //================================================================================
329 /*!
330  * \brief Fills myListWidget
331  */
332 //================================================================================
333
334 void StdMeshersGUI_PropagationHelperWdg::updateList(bool enable)
335 {
336   buildChains();
337
338   myListWidget->clear();
339
340   if ( enable )
341   {
342     QListWidgetItem* item;
343     if ( myChains.empty() || !enable )
344     {
345       item = new QListWidgetItem(tr("NO_CHAINS"), myListWidget );
346       item->setData( Qt::UserRole, -1 );
347     }
348     else
349       for ( size_t i = 0; i < myChains.size(); ++i )
350       {
351         QString text = tr( "CHAIN_NUM_NB_EDGES" ).arg( i+1 ).arg( myChains[i].size() );
352         item = new QListWidgetItem( text, myListWidget );
353         item->setData( Qt::UserRole, (int) i );
354       }
355   }
356   else
357   {
358     onListSelectionChanged();
359   }
360 }
361
362 //================================================================================
363 /*!
364  * \brief Returns ids of a selected chain
365  */
366 //================================================================================
367
368 std::vector< int > * StdMeshersGUI_PropagationHelperWdg::getSelectedChain()
369 {
370   std::vector< int > * chain = 0;
371   if ( QListWidgetItem * item = myListWidget->currentItem() )
372   {
373     int i = item->data( Qt::UserRole ).toInt();
374     if ( 0 <= i && i < myChains.size() )
375       chain = & myChains[i];
376   }
377   return chain;
378 }
379
380 //================================================================================
381 /*!
382  * \brief SLOT called when a selected chain changes
383  */
384 //================================================================================
385
386 void StdMeshersGUI_PropagationHelperWdg::onListSelectionChanged()
387 {
388   if ( !mySubSelectWdg ) return;
389   SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
390   if ( !previewActor ) return;
391
392   bool hasReversedEdges = false;
393   const std::vector< int > * chain = getSelectedChain();
394   if ( chain )
395   {
396     SUIT_OverrideCursor wc;
397
398     TopoDS_Compound aCompound;
399     BRep_Builder aBuilder;
400     aBuilder.MakeCompound( aCompound );
401
402     for ( size_t i = 0; i < chain->size(); ++i )
403     {
404       int iE = Abs( (*chain)[ i ]);
405       TopoDS_Shape E = previewActor->GetShapeByIndex( iE );
406       if ( !E.IsNull() && E.ShapeType() == TopAbs_EDGE )
407       {
408         E.Orientation( (*chain)[ i ] < 0 ? TopAbs_REVERSED : TopAbs_FORWARD );
409         aBuilder.Add( aCompound, E );
410         if ( (*chain)[ i ] < 0 )
411           hasReversedEdges = true;
412       }
413     }
414     if ( myActor )
415     {
416       // SetShape() to an existing actor leads to a wrong FitAll
417       myActor->RemoveFromRender( myRenderer );
418       myActor->Delete();
419     }
420     myActor = GEOM_Actor::New();
421     myActor->SetShape( aCompound, 0, true );
422     myActor->SetIsolatedEdgeColor( 1, 0, 1 );
423     myActor->SetWidth( 2 );
424     myActor->SetVectorMode( true );
425     myActor->SetPickable( false );
426
427     if (( myRenderer = mySubSelectWdg->GetRenderer() ))
428       myActor->AddToRender( myRenderer );
429
430     if ( LightApp_SelectionMgr* selMrg = SMESHGUI::selectionMgr())
431     {
432       selMrg->clearSelected();
433       mySubSelectWdg->ClearSelected(); /* call this because the above call does not
434                                           lead to currentSelectionChanged signal (bug?)*/
435     }
436   }
437   bool enableButtons = chain;
438   if ( chain )
439     enableButtons = myListWidget->currentItem()->data( Qt::UserRole+1 ).isNull();
440
441   myAddButton->setEnabled( enableButtons && hasReversedEdges );
442   myReverseButton->setEnabled( enableButtons );
443
444
445   bool toShowChain = chain;
446
447   if ( myActor )
448     myActor->SetVisibility( toShowChain );
449
450   previewActor->SetShown( !toShowChain );
451
452   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow())
453     aViewWindow->Repaint();
454 }
455
456 //================================================================================
457 /*!
458  * \brief SLOT called when 'Add' button is clicked
459  */
460 //================================================================================
461
462 void StdMeshersGUI_PropagationHelperWdg::onAdd()
463 {
464   const std::vector< int > * chain = getSelectedChain();
465   if ( !chain || !mySubSelectWdg ) return;
466
467   // join current and new IDs
468
469   SMESH::long_array_var ids = mySubSelectWdg->GetListOfIDs();
470
471   std::set< int > idSet;
472   for ( int i = 0, nb = ids->length(); i < nb; ++i )
473     idSet.insert( idSet.end(), ids[i] );
474
475   for ( size_t i = 0; i < chain->size(); ++i )
476     if ( (*chain)[ i ] < 0 )
477       idSet.insert( -1 * (*chain)[ i ]);
478
479   if ( ids->length() != idSet.size() )
480   {
481     ids->length( idSet.size() );
482     std::set< int >::iterator id = idSet.begin();
483     for ( int i = 0, nb = ids->length(); i < nb; ++i, ++id )
484       ids[ i ] = *id;
485
486     mySubSelectWdg->SetListOfIDs( ids );
487   }
488   mySubSelectWdg->ClearSelected();
489
490   if ( QListWidgetItem * item = myListWidget->currentItem() )
491   {
492     //delete item;
493     item->setForeground( QBrush( QColor( 100, 100, 100 )));
494     item->setData( Qt::UserRole+1, 1 );
495   }
496   myAddButton->setEnabled( false );
497   myReverseButton->setEnabled( false );
498 }
499
500 //================================================================================
501 /*!
502  * \brief SLOT called when 'Reverse' button is clicked
503  */
504 //================================================================================
505
506 void StdMeshersGUI_PropagationHelperWdg::onReverse()
507 {
508   if ( std::vector< int > * chain = getSelectedChain())
509   {
510     for ( size_t i = 0; i < chain->size(); ++i )
511       (*chain)[ i ] *= -1;
512
513     onListSelectionChanged();
514   }
515 }