Salome HOME
Merge branch 'V8_4_BR'
[modules/smesh.git] / src / StdMeshersGUI / StdMeshersGUI_PropagationHelperWdg.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // 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     TopTools_MapOfShape edgesMap;
178     TopExp_Explorer edge( mainShape, TopAbs_EDGE );
179     for ( ; edge.More(); edge.Next() )
180       if ( edgesMap.Add( edge.Current() ))
181         aBuilder.Add( aCompound, edge.Current() );
182
183     myModelActor = GEOM_Actor::New();
184     myModelActor->SetShape( aCompound, 0, 0 );
185     myModelActor->SetPickable( false );
186     myModelActor->SetIsolatedEdgeColor( 0.5, 0.5, 0.5 );
187
188     if (( myRenderer = mySubSelectWdg->GetRenderer() ))
189       myModelActor->AddToRender( myRenderer );
190   }
191
192   if ( myModelActor )
193     myModelActor->SetVisibility( toShow );
194
195   SMESH::RepaintCurrentView();
196 }
197
198 //================================================================================
199 /*!
200  * \brief Build propagation chains. Return false if no chains found
201  */
202 //================================================================================
203
204 bool StdMeshersGUI_PropagationHelperWdg::buildChains()
205 {
206   if ( !myChains.empty() ) return false;
207
208   if ( !mySubSelectWdg ) return false;
209
210   TopoDS_Shape shape     = mySubSelectWdg->GetGeomShape();
211   TopoDS_Shape mainShape = mySubSelectWdg->GetMainShape();
212   if ( shape.IsNull() && mainShape.IsNull() ) return false;
213
214   if ( shape.IsNull() )     shape = mainShape;
215   if ( mainShape.IsNull() ) mainShape = shape;
216
217   SUIT_OverrideCursor wc;
218
219   // aPreviewActor holds a map od all sub-shapes of mainShape
220   SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
221   if ( !previewActor ) return false;
222   const QList<int>& edgeIDs = previewActor->GetIndices();
223
224   // Make a 'map' of WIREs of EDGE with quadrilateral WIREs only
225
226   typedef int                        TGeomID; // index in the mainShape
227   typedef std::vector< TGeomID >     TWire; // signed IDs of EDGEs, sign means orientation
228   typedef std::pair< int, TWire* >   TEdgeInWire; // index in TWire + TWire*
229   typedef std::vector< TEdgeInWire > TWiresOfEdge;// WIREs including an EDGE
230
231   std::vector< TWire > quadWires;
232   quadWires.reserve( previewActor->NbShapesOfType( TopAbs_FACE ));
233   NCollection_DataMap< TGeomID, TWiresOfEdge >
234     wiresOfEdge( previewActor->NbShapesOfType( TopAbs_EDGE ));
235
236   TopExp_Explorer wire;
237   TopTools_MapOfShape faceMap;
238   for ( TopExp_Explorer face( mainShape, TopAbs_FACE ); face.More(); face.Next() )
239   {
240     if ( !faceMap.Add( face.Current() )) continue;
241
242     wire.Init( face.Current(), TopAbs_WIRE );
243     TopoDS_Shape W = wire.Current().Oriented( TopAbs_FORWARD );
244
245     wire.Next();
246     if ( wire.More() ) continue;
247
248     // count EDGEs
249     int nbE = 0;
250     for ( TopoDS_Iterator edge( W, false, false ); edge.More() && nbE < 5; ++nbE )
251       edge.Next();
252     if ( nbE != 4 ) continue;
253
254     // fill a TWire and TWiresOfEdge
255     quadWires.push_back( TWire() );
256     TWire& wire = quadWires.back();
257     wire.reserve( 4 );
258     for ( BRepTools_WireExplorer edge( TopoDS::Wire( W )); edge.More(); edge.Next() )
259     {
260       const TopoDS_Shape& E = edge.Current();
261       int iE = previewActor->GetIndexByShape( E );
262       if ( iE < 1 )
263         continue;
264       if ( !wiresOfEdge.IsBound( iE ))
265         wiresOfEdge.Bind( iE, TWiresOfEdge() );
266       wiresOfEdge( iE ).push_back( TEdgeInWire( wire.size(), & wire ));
267
268       wire.push_back( E.Orientation() == TopAbs_REVERSED ? -iE : iE );
269     }
270   }
271   if ( quadWires.empty() )
272     return false;
273
274   // Find chains
275
276   TColStd_IndexedMapOfInteger chain, chainedEdges;
277
278   TColStd_MapOfInteger shapeEdges;
279   if ( !shape.IsSame( mainShape ))
280     for ( QList<TGeomID>::const_iterator ieIt = edgeIDs.begin(); ieIt != edgeIDs.end(); ++ieIt )
281       shapeEdges.Add( *ieIt );
282
283   // loop on all EDGEs in mainShape
284   for ( QList<TGeomID>::const_iterator ieIt = edgeIDs.begin(); ieIt != edgeIDs.end(); ++ieIt )
285   {
286     if ( chainedEdges.Contains( *ieIt ))
287       continue;
288     // start a new chain
289     chain.Clear();
290     chain.Add( *ieIt );
291     chainedEdges.Add( *ieIt );
292     for ( int iC = 1; iC <= chain.Extent(); ++iC ) // loop on EDGE's in chain
293     {
294       TGeomID iE = chain( iC ), iEAbs = Abs( iE );
295       if ( !wiresOfEdge.IsBound( iEAbs ))
296         continue;
297       const TWiresOfEdge& wires = wiresOfEdge( iEAbs );
298       for (size_t i = 0; i < wires.size(); ++i ) // loop on WIREs sharing iE
299       {
300         const TEdgeInWire& eInW = wires[i];
301         const TWire&    W = *eInW.second;
302         if ( W.size() != 4 ) continue;
303         const int    iInW = eInW.first;
304         const int iInWOpp = ( iInW + 2 ) % 4; // an opposite edge index
305         TGeomID  iEOppAbs = Abs( W[ iInWOpp ] );
306
307         int prevNbChained = chainedEdges.Extent();
308         if ( prevNbChained < chainedEdges.Add( iEOppAbs ))
309         {
310           int dir = iE / iEAbs;
311           bool isSameDir = ( W[ iInW ] * W[ iInWOpp ] < 0 );
312           if ( !isSameDir )
313             dir *= -1;
314           chain.Add( dir * iEOppAbs );
315         }
316       }
317     }
318     // store a chain
319     if ( chain.Extent() > 1 )
320     {
321       myChains.push_back( std::vector< TGeomID >() );
322       std::vector< TGeomID > & ch = myChains.back();
323       for ( int iC = 1; iC <= chain.Extent(); ++iC )
324       {
325         TGeomID iE = chain( iC );
326         if ( shapeEdges.IsEmpty() || shapeEdges.Contains( Abs( iE )))
327           ch.push_back( iE );
328       }
329       if ( ch.size() < 2 )
330         myChains.pop_back();
331     }
332   } // loop on edgeIDs
333
334   return !myChains.empty();
335 }
336
337 //================================================================================
338 /*!
339  * \brief Fills myListWidget
340  */
341 //================================================================================
342
343 void StdMeshersGUI_PropagationHelperWdg::updateList(bool enable)
344 {
345   buildChains();
346
347   myListWidget->clear();
348
349   if ( enable )
350   {
351     QListWidgetItem* item;
352     if ( myChains.empty() || !enable )
353     {
354       item = new QListWidgetItem(tr("NO_CHAINS"), myListWidget );
355       item->setData( Qt::UserRole, -1 );
356     }
357     else
358       for ( size_t i = 0; i < myChains.size(); ++i )
359       {
360         QString text = tr( "CHAIN_NUM_NB_EDGES" ).arg( i+1 ).arg( myChains[i].size() );
361         item = new QListWidgetItem( text, myListWidget );
362         item->setData( Qt::UserRole, (int) i );
363       }
364   }
365   else
366   {
367     onListSelectionChanged();
368   }
369 }
370
371 //================================================================================
372 /*!
373  * \brief Returns ids of a selected chain
374  */
375 //================================================================================
376
377 std::vector< int > * StdMeshersGUI_PropagationHelperWdg::getSelectedChain()
378 {
379   std::vector< int > * chain = 0;
380   if ( QListWidgetItem * item = myListWidget->currentItem() )
381   {
382     size_t i = (size_t) item->data( Qt::UserRole ).toInt();
383     if ( 0 <= i && i < myChains.size() )
384       chain = & myChains[i];
385   }
386   return chain;
387 }
388
389 //================================================================================
390 /*!
391  * \brief SLOT called when a selected chain changes
392  */
393 //================================================================================
394
395 void StdMeshersGUI_PropagationHelperWdg::onListSelectionChanged()
396 {
397   if ( !mySubSelectWdg ) return;
398   SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
399   if ( !previewActor ) return;
400
401   bool hasReversedEdges = false;
402   const std::vector< int > * chain = getSelectedChain();
403   if ( chain )
404   {
405     SUIT_OverrideCursor wc;
406
407     TopoDS_Compound aCompound;
408     BRep_Builder aBuilder;
409     aBuilder.MakeCompound( aCompound );
410
411     for ( size_t i = 0; i < chain->size(); ++i )
412     {
413       int iE = Abs( (*chain)[ i ]);
414       TopoDS_Shape E = previewActor->GetShapeByIndex( iE );
415       if ( !E.IsNull() && E.ShapeType() == TopAbs_EDGE )
416       {
417         E.Orientation( (*chain)[ i ] < 0 ? TopAbs_REVERSED : TopAbs_FORWARD );
418         aBuilder.Add( aCompound, E );
419         if ( (*chain)[ i ] < 0 )
420           hasReversedEdges = true;
421       }
422     }
423     if ( myActor )
424     {
425       // SetShape() to an existing actor leads to a wrong FitAll
426       myActor->RemoveFromRender( myRenderer );
427       myActor->Delete();
428     }
429     myActor = GEOM_Actor::New();
430     myActor->SetShape( aCompound, 0, true );
431     myActor->SetIsolatedEdgeColor( 1, 0, 1 );
432     myActor->SetWidth( 2 );
433     myActor->SetVectorMode( true );
434     myActor->SetPickable( false );
435
436     if (( myRenderer = mySubSelectWdg->GetRenderer() ))
437       myActor->AddToRender( myRenderer );
438
439     if ( LightApp_SelectionMgr* selMrg = SMESHGUI::selectionMgr())
440     {
441       selMrg->clearSelected();
442       mySubSelectWdg->ClearSelected(); /* call this because the above call does not
443                                           lead to currentSelectionChanged signal (bug?)*/
444     }
445   }
446   bool enableButtons = chain;
447   if ( chain )
448     enableButtons = myListWidget->currentItem()->data( Qt::UserRole+1 ).isNull();
449
450   myAddButton->setEnabled( enableButtons && hasReversedEdges );
451   myReverseButton->setEnabled( enableButtons );
452
453
454   bool toShowChain = chain;
455
456   if ( myActor )
457     myActor->SetVisibility( toShowChain );
458
459   previewActor->SetShown( !toShowChain );
460
461   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow())
462     aViewWindow->Repaint();
463 }
464
465 //================================================================================
466 /*!
467  * \brief SLOT called when 'Add' button is clicked
468  */
469 //================================================================================
470
471 void StdMeshersGUI_PropagationHelperWdg::onAdd()
472 {
473   const std::vector< int > * chain = getSelectedChain();
474   if ( !chain || !mySubSelectWdg ) return;
475
476   // join current and new IDs
477
478   SMESH::long_array_var ids = mySubSelectWdg->GetListOfIDs();
479
480   std::set< int > idSet;
481   for ( int i = 0, nb = ids->length(); i < nb; ++i )
482     idSet.insert( idSet.end(), ids[i] );
483
484   for ( size_t i = 0; i < chain->size(); ++i )
485     if ( (*chain)[ i ] < 0 )
486       idSet.insert( -1 * (*chain)[ i ]);
487
488   if ( ids->length() != idSet.size() )
489   {
490     ids->length( idSet.size() );
491     std::set< int >::iterator id = idSet.begin();
492     for ( int i = 0, nb = ids->length(); i < nb; ++i, ++id )
493       ids[ i ] = *id;
494
495     mySubSelectWdg->SetListOfIDs( ids );
496   }
497   mySubSelectWdg->ClearSelected();
498
499   if ( QListWidgetItem * item = myListWidget->currentItem() )
500   {
501     //delete item;
502     item->setForeground( QBrush( QColor( 100, 100, 100 )));
503     item->setData( Qt::UserRole+1, 1 );
504   }
505   myAddButton->setEnabled( false );
506   myReverseButton->setEnabled( false );
507 }
508
509 //================================================================================
510 /*!
511  * \brief SLOT called when 'Reverse' button is clicked
512  */
513 //================================================================================
514
515 void StdMeshersGUI_PropagationHelperWdg::onReverse()
516 {
517   if ( std::vector< int > * chain = getSelectedChain())
518   {
519     for ( size_t i = 0; i < chain->size(); ++i )
520       (*chain)[ i ] *= -1;
521
522     onListSelectionChanged();
523   }
524 }