+// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File : StdMeshersGUI_PropagationHelperWdg.cxx
+// Created : Thu Mar 19 18:46:24 2015
+// Author : Edward AGAPOV (eap)
+
+#include "StdMeshersGUI_PropagationHelperWdg.h"
+
+#include "StdMeshersGUI_SubShapeSelectorWdg.h"
+#include "SMESH_PreviewActorsCollection.h"
+#include "SMESHGUI_VTKUtils.h"
+
+#include <GEOM_Actor.h>
+
+#include <LightApp_SelectionMgr.h>
+#include <SUIT_OverrideCursor.h>
+#include <SVTK_ViewWindow.h>
+#include <vtkRenderer.h>
+
+#include <QCheckBox>
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QLabel>
+#include <QList>
+#include <QListWidget>
+#include <QListWidgetItem>
+#include <QPushButton>
+
+#include <BRepTools_WireExplorer.hxx>
+#include <BRep_Builder.hxx>
+#include <NCollection_DataMap.hxx>
+#include <TColStd_IndexedMapOfInteger.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Compound.hxx>
+#include <TopoDS_Iterator.hxx>
+
+#include <set>
+
+#define SPACING 6
+#define MARGIN 11
+
+//================================================================================
+/*!
+ * \brief Constructor
+ */
+//================================================================================
+
+StdMeshersGUI_PropagationHelperWdg::
+StdMeshersGUI_PropagationHelperWdg( StdMeshersGUI_SubShapeSelectorWdg* subSelectWdg,
+ QWidget* parent ):
+ QWidget( parent ), mySubSelectWdg( subSelectWdg ), myActor( 0 ), myModelActor( 0 )
+{
+ QGroupBox* helperBox = new QGroupBox( tr("HELPER"), this );
+ QCheckBox* showGeomChkBox = new QCheckBox( tr("SHOW_GEOMETRY"), helperBox );
+ QGroupBox* chainBox = new QGroupBox( tr("PROPAGATION_CHAINS"), helperBox );
+ chainBox->setCheckable( true );
+ chainBox->setChecked( false );
+ myListWidget = new QListWidget( helperBox );
+ myListWidget->setSelectionMode( QAbstractItemView::SingleSelection );
+ myAddButton = new QPushButton( tr("ADD"), helperBox );
+ myReverseButton = new QPushButton( tr("REVERSE"), helperBox );
+
+ QGridLayout* chainsLayout = new QGridLayout( chainBox );
+ chainsLayout->setMargin( MARGIN );
+ chainsLayout->setSpacing( SPACING );
+ chainsLayout->addWidget(myListWidget, 0, 0, 3, 3);
+ chainsLayout->addWidget(myAddButton, 0, 3);
+ chainsLayout->addWidget(myReverseButton, 1, 3);
+
+ QVBoxLayout* helperLayout = new QVBoxLayout( helperBox );
+ helperLayout->setMargin( MARGIN );
+ helperLayout->setSpacing( SPACING );
+ helperLayout->addWidget( showGeomChkBox );
+ helperLayout->addWidget( chainBox );
+
+ QVBoxLayout* lay = new QVBoxLayout( this );
+ lay->setMargin( 0 );
+ lay->setSpacing( SPACING );
+ lay->addWidget( helperBox );
+
+ connect( showGeomChkBox, SIGNAL( toggled(bool)), SLOT( onShowGeometry(bool)));
+ connect( chainBox, SIGNAL( toggled(bool)), SLOT( updateList(bool)));
+ connect( myListWidget, SIGNAL( itemSelectionChanged()), SLOT( onListSelectionChanged() ));
+ connect( myAddButton, SIGNAL( clicked(bool)), SLOT( onAdd() ));
+ connect( myReverseButton, SIGNAL( clicked(bool)), SLOT( onReverse() ));
+
+ onListSelectionChanged();
+}
+
+//================================================================================
+/*!
+ * \brief Destructor
+ */
+//================================================================================
+
+StdMeshersGUI_PropagationHelperWdg::~StdMeshersGUI_PropagationHelperWdg()
+{
+ if ( myActor )
+ {
+ myActor->RemoveFromRender( myRenderer );
+ myActor->Delete();
+ myActor = 0;
+ }
+ if ( myModelActor )
+ {
+ myModelActor->RemoveFromRender( myRenderer );
+ myModelActor->Delete();
+ myModelActor = 0;
+ }
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when 'Show Geometry' is checked
+ */
+//================================================================================
+
+void StdMeshersGUI_PropagationHelperWdg::onShowGeometry(bool toShow)
+{
+ if ( ! myModelActor )
+ {
+ TopoDS_Shape shape = mySubSelectWdg->GetGeomShape();
+ TopoDS_Shape mainShape = mySubSelectWdg->GetMainShape();
+ if ( shape.IsNull() && mainShape.IsNull() ) return;
+ if ( mainShape.IsNull() ) mainShape = shape;
+
+ SUIT_OverrideCursor wc;
+
+ TopoDS_Compound aCompound;
+ BRep_Builder aBuilder;
+ aBuilder.MakeCompound( aCompound );
+
+ SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
+ if ( !previewActor ) return;
+ const QList<int>& egdeIDs = previewActor->GetIndices();
+ for ( QList<int>::const_iterator ieIt = egdeIDs.begin(); ieIt != egdeIDs.end(); ++ieIt )
+ {
+ TopoDS_Shape E = previewActor->GetShapeByIndex( *ieIt );
+ if ( !E.IsNull() && E.ShapeType() == TopAbs_EDGE )
+ aBuilder.Add( aCompound, E );
+ }
+
+ myModelActor = GEOM_Actor::New();
+ myModelActor->SetShape( aCompound, 0, 0 );
+ myModelActor->SetPickable( false );
+ myModelActor->SetIsolatedEdgeColor( 0.5, 0.5, 0.5 );
+
+ if (( myRenderer = mySubSelectWdg->GetRenderer() ))
+ myModelActor->AddToRender( myRenderer );
+ }
+
+ if ( myModelActor )
+ myModelActor->SetVisibility( toShow );
+
+ SMESH::RepaintCurrentView();
+}
+
+//================================================================================
+/*!
+ * \brief Build propagation chains. Return false if no chains found
+ */
+//================================================================================
+
+bool StdMeshersGUI_PropagationHelperWdg::buildChains()
+{
+ if ( !myChains.empty() ) return false;
+
+ if ( !mySubSelectWdg ) return false;
+
+ TopoDS_Shape shape = mySubSelectWdg->GetGeomShape();
+ TopoDS_Shape mainShape = mySubSelectWdg->GetMainShape();
+ if ( shape.IsNull() && mainShape.IsNull() ) return false;
+
+ if ( shape.IsNull() ) shape = mainShape;
+ if ( mainShape.IsNull() ) mainShape = shape;
+
+ SUIT_OverrideCursor wc;
+
+ // aPreviewActor holds a map od all sub-shapes of mainShape
+ SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
+ if ( !previewActor ) return false;
+ const QList<int>& egdeIDs = previewActor->GetIndices();
+
+ // Make a 'map' of WIREs of EDGE with quadrilateral WIREs only
+
+ typedef int TGeomID; // index in the mainShape
+ typedef std::vector< TGeomID > TWire; // signed IDs of EDGEs, sign means orientation
+ typedef std::pair< int, TWire* > TEdgeInWire; // index in TWire + TWire*
+ typedef std::vector< TEdgeInWire > TWiresOfEdge;// WIREs including an EDGE
+
+ std::vector< TWire > quadWires;
+ quadWires.reserve( egdeIDs.count() );
+ NCollection_DataMap< TGeomID, TWiresOfEdge > wiresOfEdge( egdeIDs.count() );
+
+ TopExp_Explorer wire;
+ for ( TopExp_Explorer face( shape, TopAbs_FACE ); face.More(); face.Next() )
+ {
+ wire.Init( face.Current(), TopAbs_WIRE );
+ TopoDS_Shape W = wire.Current().Oriented( TopAbs_FORWARD );
+
+ wire.Next();
+ if ( wire.More() ) continue;
+
+ // count EDGEs
+ int nbE = 0;
+ for ( TopoDS_Iterator edge( W, false, false ); edge.More() && nbE < 5; ++nbE )
+ edge.Next();
+ if ( nbE != 4 ) continue;
+
+ // fill a TWire and TWiresOfEdge
+ quadWires.push_back( TWire() );
+ TWire& wire = quadWires.back();
+ wire.reserve( 4 );
+ for ( BRepTools_WireExplorer edge( TopoDS::Wire( W )); edge.More(); edge.Next() )
+ {
+ const TopoDS_Shape& E = edge.Current();
+ int iE = previewActor->GetIndexByShape( E );
+ if ( iE < 1 )
+ continue;
+ if ( !wiresOfEdge.IsBound( iE ))
+ wiresOfEdge.Bind( iE, TWiresOfEdge() );
+ wiresOfEdge( iE ).push_back( TEdgeInWire( wire.size(), & wire ));
+
+ wire.push_back( E.Orientation() == TopAbs_REVERSED ? -iE : iE );
+ }
+ }
+ if ( quadWires.empty() )
+ return false;
+
+ // Find chains
+
+ TColStd_IndexedMapOfInteger chain, chainedEdges;
+
+ // loop on all EDGEs in mainShape
+ for ( QList<TGeomID>::const_iterator ieIt = egdeIDs.begin(); ieIt != egdeIDs.end(); ++ieIt )
+ {
+ if ( chainedEdges.Contains( *ieIt ))
+ continue;
+ // start a new chain
+ chain.Clear();
+ chain.Add( *ieIt );
+ chainedEdges.Add( *ieIt );
+ for ( int iC = 1; iC <= chain.Extent(); ++iC ) // loop on EDGE's in chain
+ {
+ TGeomID iE = chain( iC ), iEAbs = Abs( iE );
+ if ( !wiresOfEdge.IsBound( iEAbs ))
+ continue;
+ const TWiresOfEdge& wires = wiresOfEdge( iEAbs );
+ for (size_t i = 0; i < wires.size(); ++i ) // loop on WIREs sharing iE
+ {
+ const TEdgeInWire& eInW = wires[i];
+ const TWire& W = *eInW.second;
+ if ( W.size() != 4 ) continue;
+ const int iInW = eInW.first;
+ const int iInWOpp = ( iInW + 2 ) % 4; // an opposite edge index
+ TGeomID iEOppAbs = Abs( W[ iInWOpp ] );
+
+ int prevNbChained = chainedEdges.Extent();
+ if ( prevNbChained < chainedEdges.Add( iEOppAbs ))
+ {
+ int dir = iE / iEAbs;
+ bool isSameDir = ( W[ iInW ] * W[ iInWOpp ] < 0 );
+ if ( !isSameDir )
+ dir *= -1;
+ chain.Add( dir * iEOppAbs );
+ }
+ }
+ }
+ // store a chain
+ if ( chain.Extent() > 1 )
+ {
+ myChains.push_back( std::vector< TGeomID >() );
+ std::vector< TGeomID > & ch = myChains.back();
+ for ( int iC = 1; iC <= chain.Extent(); ++iC )
+ ch.push_back( chain( iC ) );
+ }
+ }
+ return !myChains.empty();
+}
+
+//================================================================================
+/*!
+ * \brief Fills myListWidget
+ */
+//================================================================================
+
+void StdMeshersGUI_PropagationHelperWdg::updateList(bool enable)
+{
+ buildChains();
+
+ myListWidget->clear();
+
+ if ( enable )
+ {
+ QListWidgetItem* item;
+ if ( myChains.empty() || !enable )
+ {
+ item = new QListWidgetItem(tr("NO_CHAINS"), myListWidget );
+ item->setData( Qt::UserRole, -1 );
+ }
+ else
+ for ( size_t i = 0; i < myChains.size(); ++i )
+ {
+ QString text = tr( "CHAIN_NUM_NB_EDGES" ).arg( i+1 ).arg( myChains[i].size() );
+ item = new QListWidgetItem( text, myListWidget );
+ item->setData( Qt::UserRole, (int) i );
+ }
+ }
+ else
+ {
+ onListSelectionChanged();
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Returns ids of a selected chain
+ */
+//================================================================================
+
+std::vector< int > * StdMeshersGUI_PropagationHelperWdg::getSelectedChain()
+{
+ std::vector< int > * chain = 0;
+ if ( QListWidgetItem * item = myListWidget->currentItem() )
+ {
+ int i = item->data( Qt::UserRole ).toInt();
+ if ( 0 <= i && i < myChains.size() )
+ chain = & myChains[i];
+ }
+ return chain;
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when a selected chain changes
+ */
+//================================================================================
+
+void StdMeshersGUI_PropagationHelperWdg::onListSelectionChanged()
+{
+ if ( !mySubSelectWdg ) return;
+ SMESH_PreviewActorsCollection* previewActor = mySubSelectWdg->GetActorCollection();
+ if ( !previewActor ) return;
+
+ bool hasReversedEdges = false;
+ const std::vector< int > * chain = getSelectedChain();
+ if ( chain )
+ {
+ SUIT_OverrideCursor wc;
+
+ TopoDS_Compound aCompound;
+ BRep_Builder aBuilder;
+ aBuilder.MakeCompound( aCompound );
+
+ for ( size_t i = 0; i < chain->size(); ++i )
+ {
+ int iE = Abs( (*chain)[ i ]);
+ TopoDS_Shape E = previewActor->GetShapeByIndex( iE );
+ if ( !E.IsNull() && E.ShapeType() == TopAbs_EDGE )
+ {
+ E.Orientation( (*chain)[ i ] < 0 ? TopAbs_REVERSED : TopAbs_FORWARD );
+ aBuilder.Add( aCompound, E );
+ if ( (*chain)[ i ] < 0 )
+ hasReversedEdges = true;
+ }
+ }
+ if ( myActor )
+ {
+ // SetShape() to an existing actor leads to a wrong FitAll
+ myActor->RemoveFromRender( myRenderer );
+ myActor->Delete();
+ }
+ myActor = GEOM_Actor::New();
+ myActor->SetShape( aCompound, 0, true );
+ myActor->SetIsolatedEdgeColor( 1, 0, 1 );
+ myActor->SetWidth( 2 );
+ myActor->SetVectorMode( true );
+ myActor->SetPickable( false );
+
+ if (( myRenderer = mySubSelectWdg->GetRenderer() ))
+ myActor->AddToRender( myRenderer );
+
+ if ( LightApp_SelectionMgr* selMrg = SMESHGUI::selectionMgr())
+ {
+ selMrg->clearSelected();
+ mySubSelectWdg->ClearSelected(); /* call this because the above call does not
+ lead to currentSelectionChanged signal (bug?)*/
+ }
+ }
+ bool enableButtons = chain;
+ if ( chain )
+ enableButtons = myListWidget->currentItem()->data( Qt::UserRole+1 ).isNull();
+
+ myAddButton->setEnabled( enableButtons && hasReversedEdges );
+ myReverseButton->setEnabled( enableButtons );
+
+
+ bool toShowChain = chain;
+
+ if ( myActor )
+ myActor->SetVisibility( toShowChain );
+
+ previewActor->SetShown( !toShowChain );
+
+ if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow())
+ aViewWindow->Repaint();
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when 'Add' button is clicked
+ */
+//================================================================================
+
+void StdMeshersGUI_PropagationHelperWdg::onAdd()
+{
+ const std::vector< int > * chain = getSelectedChain();
+ if ( !chain || !mySubSelectWdg ) return;
+
+ // join current and new IDs
+
+ SMESH::long_array_var ids = mySubSelectWdg->GetListOfIDs();
+
+ std::set< int > idSet;
+ for ( int i = 0, nb = ids->length(); i < nb; ++i )
+ idSet.insert( idSet.end(), ids[i] );
+
+ for ( size_t i = 0; i < chain->size(); ++i )
+ if ( (*chain)[ i ] < 0 )
+ idSet.insert( -1 * (*chain)[ i ]);
+
+ if ( ids->length() != idSet.size() )
+ {
+ ids->length( idSet.size() );
+ std::set< int >::iterator id = idSet.begin();
+ for ( int i = 0, nb = ids->length(); i < nb; ++i, ++id )
+ ids[ i ] = *id;
+
+ mySubSelectWdg->SetListOfIDs( ids );
+ }
+ mySubSelectWdg->ClearSelected();
+
+ if ( QListWidgetItem * item = myListWidget->currentItem() )
+ {
+ //delete item;
+ item->setForeground( QBrush( QColor( 100, 100, 100 )));
+ item->setData( Qt::UserRole+1, 1 );
+ }
+ myAddButton->setEnabled( false );
+ myReverseButton->setEnabled( false );
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when 'Reverse' button is clicked
+ */
+//================================================================================
+
+void StdMeshersGUI_PropagationHelperWdg::onReverse()
+{
+ if ( std::vector< int > * chain = getSelectedChain())
+ {
+ for ( size_t i = 0; i < chain->size(); ++i )
+ (*chain)[ i ] *= -1;
+
+ onListSelectionChanged();
+ }
+}