Salome HOME
22573: [CEA 1154] Regression on a blsurf mesh (crash on test_periodicity_LR_ONLY.py)
[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   for ( TopExp_Explorer face( shape, TopAbs_FACE ); face.More(); face.Next() )
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   // loop on all EDGEs in mainShape
279   for ( QList<TGeomID>::const_iterator ieIt = egdeIDs.begin(); ieIt != egdeIDs.end(); ++ieIt )
280   {
281     if ( chainedEdges.Contains( *ieIt ))
282       continue;
283     // start a new chain
284     chain.Clear();
285     chain.Add( *ieIt );
286     chainedEdges.Add( *ieIt );
287     for ( int iC = 1; iC <= chain.Extent(); ++iC ) // loop on EDGE's in chain
288     {
289       TGeomID iE = chain( iC ), iEAbs = Abs( iE );
290       if ( !wiresOfEdge.IsBound( iEAbs ))
291         continue;
292       const TWiresOfEdge& wires = wiresOfEdge( iEAbs );
293       for (size_t i = 0; i < wires.size(); ++i ) // loop on WIREs sharing iE
294       {
295         const TEdgeInWire& eInW = wires[i];
296         const TWire&    W = *eInW.second;
297         if ( W.size() != 4 ) continue;
298         const int    iInW = eInW.first;
299         const int iInWOpp = ( iInW + 2 ) % 4; // an opposite edge index
300         TGeomID  iEOppAbs = Abs( W[ iInWOpp ] );
301
302         int prevNbChained = chainedEdges.Extent();
303         if ( prevNbChained < chainedEdges.Add( iEOppAbs ))
304         {
305           int dir = iE / iEAbs;
306           bool isSameDir = ( W[ iInW ] * W[ iInWOpp ] < 0 );
307           if ( !isSameDir )
308             dir *= -1;
309           chain.Add( dir * iEOppAbs );
310         }
311       }
312     }
313     // store a chain
314     if ( chain.Extent() > 1 )
315     {
316       myChains.push_back( std::vector< TGeomID >() );
317       std::vector< TGeomID > & ch = myChains.back();
318       for ( int iC = 1; iC <= chain.Extent(); ++iC )
319         ch.push_back( chain( iC ) );
320     }
321   }
322   return !myChains.empty();
323 }
324
325 //================================================================================
326 /*!
327  * \brief Fills myListWidget
328  */
329 //================================================================================
330
331 void StdMeshersGUI_PropagationHelperWdg::updateList(bool enable)
332 {
333   buildChains();
334
335   myListWidget->clear();
336
337   if ( enable )
338   {
339     QListWidgetItem* item;
340     if ( myChains.empty() || !enable )
341     {
342       item = new QListWidgetItem(tr("NO_CHAINS"), myListWidget );
343       item->setData( Qt::UserRole, -1 );
344     }
345     else
346       for ( size_t i = 0; i < myChains.size(); ++i )
347       {
348         QString text = tr( "CHAIN_NUM_NB_EDGES" ).arg( i+1 ).arg( myChains[i].size() );
349         item = new QListWidgetItem( text, myListWidget );
350         item->setData( Qt::UserRole, (int) i );
351       }
352   }
353   else
354   {
355     onListSelectionChanged();
356   }
357 }
358
359 //================================================================================
360 /*!
361  * \brief Returns ids of a selected chain
362  */
363 //================================================================================
364
365 std::vector< int > * StdMeshersGUI_PropagationHelperWdg::getSelectedChain()
366 {
367   std::vector< int > * chain = 0;
368   if ( QListWidgetItem * item = myListWidget->currentItem() )
369   {
370     int i = item->data( Qt::UserRole ).toInt();
371     if ( 0 <= i && i < myChains.size() )
372       chain = & myChains[i];
373   }
374   return chain;
375 }
376
377 //================================================================================
378 /*!
379  * \brief SLOT called when a selected chain changes
380  */
381 //================================================================================
382
383 void StdMeshersGUI_PropagationHelperWdg::onListSelectionChanged()
384 {
385   if ( !mySubSelectWdg ) return;
386   SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
387   if ( !previewActor ) return;
388
389   bool hasReversedEdges = false;
390   const std::vector< int > * chain = getSelectedChain();
391   if ( chain )
392   {
393     SUIT_OverrideCursor wc;
394
395     TopoDS_Compound aCompound;
396     BRep_Builder aBuilder;
397     aBuilder.MakeCompound( aCompound );
398
399     for ( size_t i = 0; i < chain->size(); ++i )
400     {
401       int iE = Abs( (*chain)[ i ]);
402       TopoDS_Shape E = previewActor->GetShapeByIndex( iE );
403       if ( !E.IsNull() && E.ShapeType() == TopAbs_EDGE )
404       {
405         E.Orientation( (*chain)[ i ] < 0 ? TopAbs_REVERSED : TopAbs_FORWARD );
406         aBuilder.Add( aCompound, E );
407         if ( (*chain)[ i ] < 0 )
408           hasReversedEdges = true;
409       }
410     }
411     if ( myActor )
412     {
413       // SetShape() to an existing actor leads to a wrong FitAll
414       myActor->RemoveFromRender( myRenderer );
415       myActor->Delete();
416     }
417     myActor = GEOM_Actor::New();
418     myActor->SetShape( aCompound, 0, true );
419     myActor->SetIsolatedEdgeColor( 1, 0, 1 );
420     myActor->SetWidth( 2 );
421     myActor->SetVectorMode( true );
422     myActor->SetPickable( false );
423
424     if (( myRenderer = mySubSelectWdg->GetRenderer() ))
425       myActor->AddToRender( myRenderer );
426
427     if ( LightApp_SelectionMgr* selMrg = SMESHGUI::selectionMgr())
428     {
429       selMrg->clearSelected();
430       mySubSelectWdg->ClearSelected(); /* call this because the above call does not
431                                           lead to currentSelectionChanged signal (bug?)*/
432     }
433   }
434   bool enableButtons = chain;
435   if ( chain )
436     enableButtons = myListWidget->currentItem()->data( Qt::UserRole+1 ).isNull();
437
438   myAddButton->setEnabled( enableButtons && hasReversedEdges );
439   myReverseButton->setEnabled( enableButtons );
440
441
442   bool toShowChain = chain;
443
444   if ( myActor )
445     myActor->SetVisibility( toShowChain );
446
447   previewActor->SetShown( !toShowChain );
448
449   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow())
450     aViewWindow->Repaint();
451 }
452
453 //================================================================================
454 /*!
455  * \brief SLOT called when 'Add' button is clicked
456  */
457 //================================================================================
458
459 void StdMeshersGUI_PropagationHelperWdg::onAdd()
460 {
461   const std::vector< int > * chain = getSelectedChain();
462   if ( !chain || !mySubSelectWdg ) return;
463
464   // join current and new IDs
465
466   SMESH::long_array_var ids = mySubSelectWdg->GetListOfIDs();
467
468   std::set< int > idSet;
469   for ( int i = 0, nb = ids->length(); i < nb; ++i )
470     idSet.insert( idSet.end(), ids[i] );
471
472   for ( size_t i = 0; i < chain->size(); ++i )
473     if ( (*chain)[ i ] < 0 )
474       idSet.insert( -1 * (*chain)[ i ]);
475
476   if ( ids->length() != idSet.size() )
477   {
478     ids->length( idSet.size() );
479     std::set< int >::iterator id = idSet.begin();
480     for ( int i = 0, nb = ids->length(); i < nb; ++i, ++id )
481       ids[ i ] = *id;
482
483     mySubSelectWdg->SetListOfIDs( ids );
484   }
485   mySubSelectWdg->ClearSelected();
486
487   if ( QListWidgetItem * item = myListWidget->currentItem() )
488   {
489     //delete item;
490     item->setForeground( QBrush( QColor( 100, 100, 100 )));
491     item->setData( Qt::UserRole+1, 1 );
492   }
493   myAddButton->setEnabled( false );
494   myReverseButton->setEnabled( false );
495 }
496
497 //================================================================================
498 /*!
499  * \brief SLOT called when 'Reverse' button is clicked
500  */
501 //================================================================================
502
503 void StdMeshersGUI_PropagationHelperWdg::onReverse()
504 {
505   if ( std::vector< int > * chain = getSelectedChain())
506   {
507     for ( size_t i = 0; i < chain->size(); ++i )
508       (*chain)[ i ] *= -1;
509
510     onListSelectionChanged();
511   }
512 }