Salome HOME
1105299a90564fb2f6623cfd2834db657f9983d9
[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   QWidget( parent ), mySubSelectWdg( subSelectWdg ), myActor( 0 ), myModelActor( 0 )
69 {
70   QGroupBox* helperBox      = new QGroupBox( tr("HELPER"), this );
71   QCheckBox* showGeomChkBox = new QCheckBox( tr("SHOW_GEOMETRY"), helperBox );
72   QGroupBox* chainBox       = new QGroupBox( tr("PROPAGATION_CHAINS"), helperBox );
73   chainBox->setCheckable( true );
74   chainBox->setChecked( false );
75   myListWidget              = new QListWidget( helperBox );
76   myListWidget->setSelectionMode( QAbstractItemView::SingleSelection );
77   myAddButton               = new QPushButton( tr("ADD"), helperBox );
78   myReverseButton           = new QPushButton( tr("REVERSE"), helperBox );
79
80   QGridLayout* chainsLayout = new QGridLayout( chainBox );
81   chainsLayout->setMargin( MARGIN );
82   chainsLayout->setSpacing( SPACING );
83   chainsLayout->addWidget(myListWidget,    0, 0, 3, 3);
84   chainsLayout->addWidget(myAddButton,     0, 3);
85   chainsLayout->addWidget(myReverseButton, 1, 3);
86
87   QVBoxLayout* helperLayout = new QVBoxLayout( helperBox );
88   helperLayout->setMargin( MARGIN );
89   helperLayout->setSpacing( SPACING );
90   helperLayout->addWidget( showGeomChkBox );
91   helperLayout->addWidget( chainBox );
92
93   QVBoxLayout* lay = new QVBoxLayout( this );
94   lay->setMargin( 0 );
95   lay->setSpacing( SPACING );
96   lay->addWidget( helperBox );
97
98   connect( showGeomChkBox,  SIGNAL( toggled(bool)), SLOT( onShowGeometry(bool)));
99   connect( chainBox,        SIGNAL( toggled(bool)), SLOT( updateList(bool)));
100   connect( myListWidget,    SIGNAL( itemSelectionChanged()), SLOT( onListSelectionChanged() ));
101   connect( myAddButton,     SIGNAL( clicked(bool)), SLOT( onAdd() ));
102   connect( myReverseButton, SIGNAL( clicked(bool)), SLOT( onReverse() ));
103
104   onListSelectionChanged();
105 }
106
107 //================================================================================
108 /*!
109  * \brief Destructor
110  */
111 //================================================================================
112
113 StdMeshersGUI_PropagationHelperWdg::~StdMeshersGUI_PropagationHelperWdg()
114 {
115   if ( myActor )
116   {
117     myActor->RemoveFromRender( myRenderer );
118     myActor->Delete();
119     myActor = 0;
120   }
121   if ( myModelActor )
122   {
123     myModelActor->RemoveFromRender( myRenderer );
124     myModelActor->Delete();
125     myModelActor = 0;
126   }
127 }
128
129 //================================================================================
130 /*!
131  * \brief SLOT called when 'Show Geometry' is checked
132  */
133 //================================================================================
134
135 void StdMeshersGUI_PropagationHelperWdg::onShowGeometry(bool toShow)
136 {
137   if ( ! myModelActor )
138   {
139     TopoDS_Shape shape     = mySubSelectWdg->GetGeomShape();
140     TopoDS_Shape mainShape = mySubSelectWdg->GetMainShape();
141     if ( shape.IsNull() && mainShape.IsNull() ) return;
142     if ( mainShape.IsNull() ) mainShape = shape;
143
144     SUIT_OverrideCursor wc;
145
146     TopoDS_Compound aCompound;
147     BRep_Builder aBuilder;
148     aBuilder.MakeCompound( aCompound );
149     
150     SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
151     if ( !previewActor ) return;
152     const QList<int>& egdeIDs = previewActor->GetIndices();
153     for ( QList<int>::const_iterator ieIt = egdeIDs.begin(); ieIt != egdeIDs.end(); ++ieIt )
154     {
155       TopoDS_Shape E = previewActor->GetShapeByIndex( *ieIt );
156       if ( !E.IsNull() && E.ShapeType() == TopAbs_EDGE )
157         aBuilder.Add( aCompound, E );
158     }
159
160     myModelActor = GEOM_Actor::New();
161     myModelActor->SetShape( aCompound, 0, 0 );
162     myModelActor->SetPickable( false );
163     myModelActor->SetIsolatedEdgeColor( 0.5, 0.5, 0.5 );
164
165     if (( myRenderer = mySubSelectWdg->GetRenderer() ))
166       myModelActor->AddToRender( myRenderer );
167   }
168
169   if ( myModelActor )
170     myModelActor->SetVisibility( toShow );
171
172   SMESH::RepaintCurrentView();
173 }
174
175 //================================================================================
176 /*!
177  * \brief Build propagation chains. Return false if no chains found
178  */
179 //================================================================================
180
181 bool StdMeshersGUI_PropagationHelperWdg::buildChains()
182 {
183   if ( !myChains.empty() ) return false;
184
185   if ( !mySubSelectWdg ) return false;
186
187   TopoDS_Shape shape     = mySubSelectWdg->GetGeomShape();
188   TopoDS_Shape mainShape = mySubSelectWdg->GetMainShape();
189   if ( shape.IsNull() && mainShape.IsNull() ) return false;
190
191   if ( shape.IsNull() )     shape = mainShape;
192   if ( mainShape.IsNull() ) mainShape = shape;
193
194   SUIT_OverrideCursor wc;
195
196   // aPreviewActor holds a map od all sub-shapes of mainShape
197   SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
198   if ( !previewActor ) return false;
199   const QList<int>& egdeIDs = previewActor->GetIndices();
200
201   // Make a 'map' of WIREs of EDGE with quadrilateral WIREs only
202
203   typedef int                        TGeomID; // index in the mainShape
204   typedef std::vector< TGeomID >     TWire; // signed IDs of EDGEs, sign means orientation
205   typedef std::pair< int, TWire* >   TEdgeInWire; // index in TWire + TWire*
206   typedef std::vector< TEdgeInWire > TWiresOfEdge;// WIREs including an EDGE
207
208   std::vector< TWire > quadWires;
209   quadWires.reserve( egdeIDs.count() );
210   NCollection_DataMap< TGeomID, TWiresOfEdge > wiresOfEdge( egdeIDs.count() );
211
212   TopExp_Explorer wire;
213   for ( TopExp_Explorer face( shape, TopAbs_FACE ); face.More(); face.Next() )
214   {
215     wire.Init( face.Current(), TopAbs_WIRE );
216     TopoDS_Shape W = wire.Current().Oriented( TopAbs_FORWARD );
217
218     wire.Next();
219     if ( wire.More() ) continue;
220
221     // count EDGEs
222     int nbE = 0;
223     for ( TopoDS_Iterator edge( W, false, false ); edge.More() && nbE < 5; ++nbE )
224       edge.Next();
225     if ( nbE != 4 ) continue;
226
227     // fill a TWire and TWiresOfEdge
228     quadWires.push_back( TWire() );
229     TWire& wire = quadWires.back();
230     wire.reserve( 4 );
231     for ( BRepTools_WireExplorer edge( TopoDS::Wire( W )); edge.More(); edge.Next() )
232     {
233       const TopoDS_Shape& E = edge.Current();
234       int iE = previewActor->GetIndexByShape( E );
235       if ( iE < 1 )
236         continue;
237       if ( !wiresOfEdge.IsBound( iE ))
238         wiresOfEdge.Bind( iE, TWiresOfEdge() );
239       wiresOfEdge( iE ).push_back( TEdgeInWire( wire.size(), & wire ));
240
241       wire.push_back( E.Orientation() == TopAbs_REVERSED ? -iE : iE );
242     }
243   }
244   if ( quadWires.empty() )
245     return false;
246
247   // Find chains
248
249   TColStd_IndexedMapOfInteger chain, chainedEdges;
250
251   // loop on all EDGEs in mainShape
252   for ( QList<TGeomID>::const_iterator ieIt = egdeIDs.begin(); ieIt != egdeIDs.end(); ++ieIt )
253   {
254     if ( chainedEdges.Contains( *ieIt ))
255       continue;
256     // start a new chain
257     chain.Clear();
258     chain.Add( *ieIt );
259     chainedEdges.Add( *ieIt );
260     for ( int iC = 1; iC <= chain.Extent(); ++iC ) // loop on EDGE's in chain
261     {
262       TGeomID iE = chain( iC ), iEAbs = Abs( iE );
263       if ( !wiresOfEdge.IsBound( iEAbs ))
264         continue;
265       const TWiresOfEdge& wires = wiresOfEdge( iEAbs );
266       for (size_t i = 0; i < wires.size(); ++i ) // loop on WIREs sharing iE
267       {
268         const TEdgeInWire& eInW = wires[i];
269         const TWire&    W = *eInW.second;
270         if ( W.size() != 4 ) continue;
271         const int    iInW = eInW.first;
272         const int iInWOpp = ( iInW + 2 ) % 4; // an opposite edge index
273         TGeomID  iEOppAbs = Abs( W[ iInWOpp ] );
274
275         int prevNbChained = chainedEdges.Extent();
276         if ( prevNbChained < chainedEdges.Add( iEOppAbs ))
277         {
278           int dir = iE / iEAbs;
279           bool isSameDir = ( W[ iInW ] * W[ iInWOpp ] < 0 );
280           if ( !isSameDir )
281             dir *= -1;
282           chain.Add( dir * iEOppAbs );
283         }
284       }
285     }
286     // store a chain
287     if ( chain.Extent() > 1 )
288     {
289       myChains.push_back( std::vector< TGeomID >() );
290       std::vector< TGeomID > & ch = myChains.back();
291       for ( int iC = 1; iC <= chain.Extent(); ++iC )
292         ch.push_back( chain( iC ) );
293     }
294   }
295   return !myChains.empty();
296 }
297
298 //================================================================================
299 /*!
300  * \brief Fills myListWidget
301  */
302 //================================================================================
303
304 void StdMeshersGUI_PropagationHelperWdg::updateList(bool enable)
305 {
306   buildChains();
307
308   myListWidget->clear();
309
310   if ( enable )
311   {
312     QListWidgetItem* item;
313     if ( myChains.empty() || !enable )
314     {
315       item = new QListWidgetItem(tr("NO_CHAINS"), myListWidget );
316       item->setData( Qt::UserRole, -1 );
317     }
318     else
319       for ( size_t i = 0; i < myChains.size(); ++i )
320       {
321         QString text = tr( "CHAIN_NUM_NB_EDGES" ).arg( i+1 ).arg( myChains[i].size() );
322         item = new QListWidgetItem( text, myListWidget );
323         item->setData( Qt::UserRole, (int) i );
324       }
325   }
326   else
327   {
328     onListSelectionChanged();
329   }
330 }
331
332 //================================================================================
333 /*!
334  * \brief Returns ids of a selected chain
335  */
336 //================================================================================
337
338 std::vector< int > * StdMeshersGUI_PropagationHelperWdg::getSelectedChain()
339 {
340   std::vector< int > * chain = 0;
341   if ( QListWidgetItem * item = myListWidget->currentItem() )
342   {
343     int i = item->data( Qt::UserRole ).toInt();
344     if ( 0 <= i && i < myChains.size() )
345       chain = & myChains[i];
346   }
347   return chain;
348 }
349
350 //================================================================================
351 /*!
352  * \brief SLOT called when a selected chain changes
353  */
354 //================================================================================
355
356 void StdMeshersGUI_PropagationHelperWdg::onListSelectionChanged()
357 {
358   if ( !mySubSelectWdg ) return;
359   SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
360   if ( !previewActor ) return;
361
362   bool hasReversedEdges = false;
363   const std::vector< int > * chain = getSelectedChain();
364   if ( chain )
365   {
366     SUIT_OverrideCursor wc;
367
368     TopoDS_Compound aCompound;
369     BRep_Builder aBuilder;
370     aBuilder.MakeCompound( aCompound );
371
372     for ( size_t i = 0; i < chain->size(); ++i )
373     {
374       int iE = Abs( (*chain)[ i ]);
375       TopoDS_Shape E = previewActor->GetShapeByIndex( iE );
376       if ( !E.IsNull() && E.ShapeType() == TopAbs_EDGE )
377       {
378         E.Orientation( (*chain)[ i ] < 0 ? TopAbs_REVERSED : TopAbs_FORWARD );
379         aBuilder.Add( aCompound, E );
380         if ( (*chain)[ i ] < 0 )
381           hasReversedEdges = true;
382       }
383     }
384     if ( myActor )
385     {
386       // SetShape() to an existing actor leads to a wrong FitAll
387       myActor->RemoveFromRender( myRenderer );
388       myActor->Delete();
389     }
390     myActor = GEOM_Actor::New();
391     myActor->SetShape( aCompound, 0, true );
392     myActor->SetIsolatedEdgeColor( 1, 0, 1 );
393     myActor->SetWidth( 2 );
394     myActor->SetVectorMode( true );
395     myActor->SetPickable( false );
396
397     if (( myRenderer = mySubSelectWdg->GetRenderer() ))
398       myActor->AddToRender( myRenderer );
399
400     if ( LightApp_SelectionMgr* selMrg = SMESHGUI::selectionMgr())
401     {
402       selMrg->clearSelected();
403       mySubSelectWdg->ClearSelected(); /* call this because the above call does not
404                                           lead to currentSelectionChanged signal (bug?)*/
405     }
406   }
407   bool enableButtons = chain;
408   if ( chain )
409     enableButtons = myListWidget->currentItem()->data( Qt::UserRole+1 ).isNull();
410
411   myAddButton->setEnabled( enableButtons && hasReversedEdges );
412   myReverseButton->setEnabled( enableButtons );
413
414
415   bool toShowChain = chain;
416
417   if ( myActor )
418     myActor->SetVisibility( toShowChain );
419
420   previewActor->SetShown( !toShowChain );
421
422   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow())
423     aViewWindow->Repaint();
424 }
425
426 //================================================================================
427 /*!
428  * \brief SLOT called when 'Add' button is clicked
429  */
430 //================================================================================
431
432 void StdMeshersGUI_PropagationHelperWdg::onAdd()
433 {
434   const std::vector< int > * chain = getSelectedChain();
435   if ( !chain || !mySubSelectWdg ) return;
436
437   // join current and new IDs
438
439   SMESH::long_array_var ids = mySubSelectWdg->GetListOfIDs();
440
441   std::set< int > idSet;
442   for ( int i = 0, nb = ids->length(); i < nb; ++i )
443     idSet.insert( idSet.end(), ids[i] );
444
445   for ( size_t i = 0; i < chain->size(); ++i )
446     if ( (*chain)[ i ] < 0 )
447       idSet.insert( -1 * (*chain)[ i ]);
448
449   if ( ids->length() != idSet.size() )
450   {
451     ids->length( idSet.size() );
452     std::set< int >::iterator id = idSet.begin();
453     for ( int i = 0, nb = ids->length(); i < nb; ++i, ++id )
454       ids[ i ] = *id;
455
456     mySubSelectWdg->SetListOfIDs( ids );
457   }
458   mySubSelectWdg->ClearSelected();
459
460   if ( QListWidgetItem * item = myListWidget->currentItem() )
461   {
462     //delete item;
463     item->setForeground( QBrush( QColor( 100, 100, 100 )));
464     item->setData( Qt::UserRole+1, 1 );
465   }
466   myAddButton->setEnabled( false );
467   myReverseButton->setEnabled( false );
468 }
469
470 //================================================================================
471 /*!
472  * \brief SLOT called when 'Reverse' button is clicked
473  */
474 //================================================================================
475
476 void StdMeshersGUI_PropagationHelperWdg::onReverse()
477 {
478   if ( std::vector< int > * chain = getSelectedChain())
479   {
480     for ( size_t i = 0; i < chain->size(); ++i )
481       (*chain)[ i ] *= -1;
482
483     onListSelectionChanged();
484   }
485 }