From 35ce25dea7ebe8d07635b52abc2cdaf5864dd966 Mon Sep 17 00:00:00 2001 From: vsr Date: Mon, 29 Oct 2012 09:05:13 +0000 Subject: [PATCH] 0021808: [CEA 662] Synchronize OCC et VTK views Integration version 1 (for testing) --- src/OCCViewer/Makefile.am | 3 +- src/OCCViewer/OCCViewer_ViewFrame.cxx | 40 ++ src/OCCViewer/OCCViewer_ViewFrame.h | 8 +- src/OCCViewer/OCCViewer_ViewPort3d.cxx | 1 + src/OCCViewer/OCCViewer_ViewWindow.cxx | 269 ++++++------- src/OCCViewer/OCCViewer_ViewWindow.h | 12 +- src/OCCViewer/resources/OCCViewer_images.ts | 4 - src/OCCViewer/resources/OCCViewer_msg_en.ts | 12 - src/OCCViewer/resources/OCCViewer_msg_fr.ts | 12 - src/SUIT/CMakeLists.txt | 2 + src/SUIT/Makefile.am | 12 +- src/SUIT/SUIT_CameraProperties.cxx | 374 ++++++++++++++++++ src/SUIT/SUIT_CameraProperties.h | 81 ++++ src/SUIT/SUIT_Tools.cxx | 24 ++ src/SUIT/SUIT_Tools.h | 7 + src/SUIT/SUIT_ViewWindow.cxx | 192 ++++++++- src/SUIT/SUIT_ViewWindow.h | 16 +- src/SUIT/resources/SUIT_images.ts | 4 + src/SUIT/resources/SUIT_msg_en.ts | 12 + src/SUIT/resources/SUIT_msg_fr.ts | 12 + .../resources/view_sync.png} | Bin src/SVTK/Makefile.am | 1 - src/SVTK/SVTK_ViewWindow.cxx | 214 ++++------ src/SVTK/SVTK_ViewWindow.h | 10 +- src/SVTK/resources/SVTK_images.ts | 4 - src/SVTK/resources/SVTK_msg_en.ts | 12 - src/SVTK/resources/SVTK_msg_fr.ts | 12 - src/SVTK/resources/vtk_view_sync.png | Bin 627 -> 0 bytes 28 files changed, 992 insertions(+), 358 deletions(-) create mode 100644 src/SUIT/SUIT_CameraProperties.cxx create mode 100644 src/SUIT/SUIT_CameraProperties.h rename src/{OCCViewer/resources/occ_view_sync.png => SUIT/resources/view_sync.png} (100%) mode change 100755 => 100644 delete mode 100755 src/SVTK/resources/vtk_view_sync.png diff --git a/src/OCCViewer/Makefile.am b/src/OCCViewer/Makefile.am index 14c993ba4..ee3f241de 100755 --- a/src/OCCViewer/Makefile.am +++ b/src/OCCViewer/Makefile.am @@ -118,8 +118,7 @@ dist_salomeres_DATA = \ resources/occ_view_style_switch.png \ resources/occ_view_zooming_style_switch.png \ resources/occ_view_maximized.png \ - resources/occ_view_minimized.png \ - resources/occ_view_sync.png + resources/occ_view_minimized.png nodist_salomeres_DATA = \ OCCViewer_images.qm \ diff --git a/src/OCCViewer/OCCViewer_ViewFrame.cxx b/src/OCCViewer/OCCViewer_ViewFrame.cxx index 2d3b66ae7..ada4a63a8 100644 --- a/src/OCCViewer/OCCViewer_ViewFrame.cxx +++ b/src/OCCViewer/OCCViewer_ViewFrame.cxx @@ -37,6 +37,7 @@ OCCViewer_ViewFrame::OCCViewer_ViewFrame(SUIT_Desktop* theDesktop, OCCViewer_Vie setCentralWidget( centralFrame ); OCCViewer_ViewWindow* view0 = theModel->createSubWindow(); + updateWindowTitle( view0 ); view0->setParent( centralFrame ); myViews.append( view0 ); // MAIN_VIEW @@ -52,6 +53,17 @@ OCCViewer_ViewFrame::~OCCViewer_ViewFrame() { } +bool OCCViewer_ViewFrame::event( QEvent* e ) +{ + if ( e->type() == QEvent::WindowTitleChange ) { + updateWindowTitle( getView( MAIN_VIEW ) ); + updateWindowTitle( getView( BOTTOM_LEFT ) ); + updateWindowTitle( getView( TOP_LEFT ) ); + updateWindowTitle( getView( TOP_RIGHT ) ); + } + return OCCViewer_ViewWindow::event( e ); +} + //************************************************************************************** OCCViewer_ViewWindow* OCCViewer_ViewFrame::getView( const int i ) const { @@ -101,6 +113,7 @@ void OCCViewer_ViewFrame::onMaximizedView( OCCViewer_ViewWindow* theView, bool i view->set2dMode( (Mode2dType) i ); view->setParent( centralWidget() ); view->setViewManager(myManager); + updateWindowTitle( view ); myViews.append( view ); aModel->initView(view); view->setMaximized(false, false); @@ -337,3 +350,30 @@ void OCCViewer_ViewFrame::setVisualParameters( const QString& parameters ) getView(MAIN_VIEW)->setVisualParameters( parameters ); } } + +SUIT_CameraProperties OCCViewer_ViewFrame::cameraProperties() +{ + // view frame does not have camera properties + return SUIT_CameraProperties(); +} + +void OCCViewer_ViewFrame::updateWindowTitle(OCCViewer_ViewWindow* theView) +{ + if ( !theView ) + return; + QString title; + switch ( theView->get2dMode() ) { + case No2dMode: + title = "3D"; break; + case XYPlane: + title = "YX"; break; + case XZPlane: + title = "XZ"; break; + case YZPlane: + title = "YZ"; break; + default: + break; + } + if ( !title.isEmpty() ) + theView->setWindowTitle( windowTitle() + " - " + title ); +} diff --git a/src/OCCViewer/OCCViewer_ViewFrame.h b/src/OCCViewer/OCCViewer_ViewFrame.h index b00bc6a8b..b9ce195e2 100644 --- a/src/OCCViewer/OCCViewer_ViewFrame.h +++ b/src/OCCViewer/OCCViewer_ViewFrame.h @@ -41,6 +41,8 @@ public: OCCViewer_ViewFrame(SUIT_Desktop* theDesktop, OCCViewer_Viewer* theModel); virtual ~OCCViewer_ViewFrame(); + bool event( QEvent* e ); + OCCViewer_ViewWindow* getView( const int ) const; virtual OCCViewer_ViewPort3d* getViewPort() { return getView(MAIN_VIEW)->getViewPort(); } @@ -82,6 +84,8 @@ public: virtual void setDropDownButtons( bool ); + virtual SUIT_CameraProperties cameraProperties(); + public slots: virtual void onFrontView() { getView(MAIN_VIEW)->onFrontView(); } virtual void onViewFitAll(); @@ -123,8 +127,8 @@ private slots: void onContextMenuRequested(QContextMenuEvent*); private: - void connectViewSignals(OCCViewer_ViewWindow* theView); - + void connectViewSignals( OCCViewer_ViewWindow* theView ); + void updateWindowTitle( OCCViewer_ViewWindow* theView ); QList myViews; QGridLayout* myLayout; diff --git a/src/OCCViewer/OCCViewer_ViewPort3d.cxx b/src/OCCViewer/OCCViewer_ViewPort3d.cxx index 3dd08a812..b41526afb 100755 --- a/src/OCCViewer/OCCViewer_ViewPort3d.cxx +++ b/src/OCCViewer/OCCViewer_ViewPort3d.cxx @@ -197,6 +197,7 @@ Handle(V3d_Viewer) OCCViewer_ViewPort3d::getViewer() const */ bool OCCViewer_ViewPort3d::syncronize( const OCCViewer_ViewPort3d* ref ) { + printf("OCCViewer_ViewPort3d::syncronize(WRONG): %x\n", this); OCCViewer_ViewPort3d* ref3d = (OCCViewer_ViewPort3d*)ref; Handle(V3d_View) refView = ref3d->getView(); Handle(V3d_View) tgtView = getView(); diff --git a/src/OCCViewer/OCCViewer_ViewWindow.cxx b/src/OCCViewer/OCCViewer_ViewWindow.cxx index 26e9ba79d..1c7c89e67 100755 --- a/src/OCCViewer/OCCViewer_ViewWindow.cxx +++ b/src/OCCViewer/OCCViewer_ViewWindow.cxx @@ -287,6 +287,8 @@ void OCCViewer_ViewWindow::initLayout() QtxAction* anAction = dynamic_cast( toolMgr()->action( GraduatedAxesId ) ); myCubeAxesDlg = new OCCViewer_CubeAxesDlg( anAction, this, "OCCViewer_CubeAxesDlg" ); myCubeAxesDlg->initialize(); + + connect( myViewPort, SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), this, SLOT( emitViewModified() ) ); } OCCViewer_ViewWindow* OCCViewer_ViewWindow::getView( const int mode ) const @@ -1240,15 +1242,8 @@ void OCCViewer_ViewWindow::createActions() connect(aAction, SIGNAL(triggered()), this, SLOT(onMaximizedView())); toolMgr()->registerAction( aAction, MaximizedId ); - // Synchronize view - aAction = new QtxAction(tr("MNU_SYNCHRONIZE_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_SYNC" ) ), - tr( "MNU_SYNCHRONIZE_VIEW" ), 0, this ); - aAction->setStatusTip(tr("DSC_SYNCHRONIZE_VIEW")); - aAction->setMenu( new QMenu( this ) ); - aAction->setCheckable(true); - connect(aAction->menu(), SIGNAL(aboutToShow()), this, SLOT(updateSyncViews())); - connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onSynchronizeView(bool))); - toolMgr()->registerAction( aAction, SynchronizeId ); + // Synchronize View + toolMgr()->registerAction( synchronizeAction(), SynchronizeId ); } /*! @@ -2493,144 +2488,142 @@ void OCCViewer_ViewWindow::updateViewAspects( const viewAspectList& aViewList ) myViewAspects = aViewList; } -void OCCViewer_ViewWindow::synchronizeView( OCCViewer_ViewWindow* viewWindow, int id ) +/*! + Get camera properties for the OCC view window. + \return shared pointer on camera properties. +*/ +SUIT_CameraProperties OCCViewer_ViewWindow::cameraProperties() { - OCCViewer_ViewWindow* otherViewWindow = 0; - QList compatibleViews; + SUIT_CameraProperties aProps; - bool isSync = viewWindow->toolMgr()->action( SynchronizeId )->isChecked(); + Handle(V3d_View) aSourceView = getViewPort()->getView(); + if ( aSourceView.IsNull() ) + return aProps; - int vwid = viewWindow->getId(); - - SUIT_Application* app = SUIT_Session::session()->activeApplication(); - if ( !app ) return; - - QList wmlist; - app->viewManagers( viewWindow->getViewManager()->getType(), wmlist ); - - foreach( SUIT_ViewManager* wm, wmlist ) { - QVector vwlist = wm->getViews(); - - foreach( SUIT_ViewWindow* vw, vwlist ) { - OCCViewer_ViewWindow* occVW = dynamic_cast( vw ); - if ( !occVW ) continue; - - // check only compatible types - occVW = occVW->getView( viewWindow->get2dMode() ); - if ( occVW ) { - if ( occVW->getId() == id ) - otherViewWindow = occVW; - else if ( occVW != viewWindow ) - compatibleViews.append( occVW ); - } - } + if ( get2dMode() == No2dMode ) { + aProps.setDimension( SUIT_CameraProperties::Dim3D ); } - - if ( isSync && id ) { - // remove all possible disconnections - foreach( OCCViewer_ViewWindow* vw, compatibleViews ) { - // disconnect target view - vw->getViewPort()->disconnect( SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), viewWindow->getViewPort(), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - viewWindow->getViewPort()->disconnect( SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), vw->getViewPort(), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - if ( otherViewWindow ) { - // disconnect source view - vw->getViewPort()->disconnect( SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), otherViewWindow->getViewPort(), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - otherViewWindow->getViewPort()->disconnect( SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), vw->getViewPort(), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - } - QAction* a = vw->toolMgr()->action( SynchronizeId ); - if ( a ) { - int anid = a->data().toInt(); - if ( a->isChecked() && ( anid == id || anid == vwid ) ) { - bool blocked = a->blockSignals( true ); - a->setChecked( false ); - a->blockSignals( blocked ); - } - } - } - if ( otherViewWindow ) { - // reconnect source and target view - otherViewWindow->getViewPort()->disconnect( SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), viewWindow->getViewPort(), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - viewWindow->getViewPort()->disconnect( SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), otherViewWindow->getViewPort(), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - otherViewWindow->getViewPort()->connect( viewWindow->getViewPort(), SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - viewWindow->getViewPort()->connect( otherViewWindow->getViewPort(), SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - // synchronize target view with source view - viewWindow->getViewPort()->synchronize( otherViewWindow->getViewPort() ); - viewWindow->toolMgr()->action( SynchronizeId )->setData( otherViewWindow->getId() ); - QAction* anOtherAcion = otherViewWindow->toolMgr()->action( SynchronizeId ); - if (anOtherAcion) { - anOtherAcion->setData( viewWindow->getId() ); - if ( !anOtherAcion->isChecked() ) { - bool blocked = anOtherAcion->blockSignals( true ); - anOtherAcion->setChecked( true ); - anOtherAcion->blockSignals( blocked ); - } - } - } - } - else if ( otherViewWindow ) { - // reconnect source and target view - otherViewWindow->getViewPort()->disconnect( SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), viewWindow->getViewPort(), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - viewWindow->getViewPort()->disconnect( SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ), otherViewWindow->getViewPort(), SLOT( synchronize( OCCViewer_ViewPort* ) ) ); - viewWindow->getViewPort()->synchronize( otherViewWindow->getViewPort() ); - viewWindow->toolMgr()->action( SynchronizeId )->setData( otherViewWindow->getId() ); - QAction* anOtherAcion = otherViewWindow->toolMgr()->action( SynchronizeId ); - if (anOtherAcion) { - if ( anOtherAcion->data().toInt() == viewWindow->getId() && anOtherAcion->isChecked() ) { - bool blocked = anOtherAcion->blockSignals( true ); - anOtherAcion->setChecked( false ); - anOtherAcion->blockSignals( blocked ); - } - } + else { + aProps.setDimension( SUIT_CameraProperties::Dim2D ); + aProps.setViewSide( (SUIT_CameraProperties::ViewSide)(int)get2dMode() ); } + + // read common properites of the view + Standard_Real anUpDir[3]; + Standard_Real aPrjDir[3]; + Standard_Real aMapScale[2]; + Standard_Real aTranslation[3]; + + aSourceView->Up(anUpDir[0], anUpDir[1], anUpDir[2]); + aSourceView->Proj(aPrjDir[0], aPrjDir[1], aPrjDir[2]); + aSourceView->At(aTranslation[0], aTranslation[1], aTranslation[2]); + aSourceView->Size(aMapScale[0], aMapScale[1]); + + // store common props + aProps.setViewUp(anUpDir[0], anUpDir[1], anUpDir[2]); + aProps.setMappingScale(aMapScale[1] / 2.0); + + // generate view orientation matrix for transforming OCC projection reference point + // into a camera (eye) position. + gp_Dir aLeftDir = gp_Dir(anUpDir[0], anUpDir[1], anUpDir[2]).Crossed( + gp_Dir(aPrjDir[0], aPrjDir[1], aPrjDir[2])); + + gp_Trsf aTrsf; + aTrsf.SetValues( aLeftDir.X(), anUpDir[0], aPrjDir[0], aTranslation[0], + aLeftDir.Y(), anUpDir[1], aPrjDir[1], aTranslation[1], + aLeftDir.Z(), anUpDir[2], aPrjDir[2], aTranslation[2], + Precision::Confusion(), + Precision::Confusion() ); + + // get projection reference point in view coordinates + Graphic3d_Vertex aProjRef = aSourceView->ViewMapping().ProjectionReferencePoint(); + + // transform to world-space coordinate system + gp_Pnt aPosition = gp_Pnt(aProjRef.X(), aProjRef.Y(), aProjRef.Z()).Transformed(aTrsf); + + // compute focal point + double aFocalPoint[3]; + + aFocalPoint[0] = aPosition.X() - aPrjDir[0] * aProjRef.Z(); + aFocalPoint[1] = aPosition.Y() - aPrjDir[1] * aProjRef.Z(); + aFocalPoint[2] = aPosition.Z() - aPrjDir[2] * aProjRef.Z(); + + aProps.setFocalPoint(aFocalPoint[0], aFocalPoint[1], aFocalPoint[2]); + aProps.setPosition(aPosition.X(), aPosition.Y(), aPosition.Z()); + + return aProps; } /*! - "Synchronize View" action slot. + Synchronize views. + This implementation synchronizes OCC view's camera propreties. */ -void OCCViewer_ViewWindow::onSynchronizeView(bool checked) +void OCCViewer_ViewWindow::synchronize( SUIT_ViewWindow* theView ) { - QAction* a = qobject_cast( sender() ); - if ( a ) { - synchronizeView( this, a->data().toInt() ); - } -} + printf("OCCViewer_ViewWindow::synchronize: %x\n", this); + bool blocked = blockSignals( true ); -/*! - Update list of available view for the "Synchronize View" action -*/ -void OCCViewer_ViewWindow::updateSyncViews() -{ - QAction* anAction = toolMgr()->action( SynchronizeId ); - if ( anAction && anAction->menu() ) { - int currentId = anAction->data().toInt(); - anAction->menu()->clear(); - SUIT_Application* app = SUIT_Session::session()->activeApplication(); - if ( app ) { - QList wmlist; - app->viewManagers( getViewManager()->getType(), wmlist ); - foreach( SUIT_ViewManager* wm, wmlist ) { - QVector vwlist = wm->getViews(); - foreach ( SUIT_ViewWindow* vw, vwlist ) { - OCCViewer_ViewWindow* occVW = dynamic_cast( vw ); - if ( !occVW || occVW == this ) continue; - // list only compatible types - OCCViewer_ViewWindow* subWindow = occVW->getView( get2dMode() ); - if ( subWindow && subWindow != this ) { - QAction* a = anAction->menu()->addAction( occVW->windowTitle() ); - if ( subWindow->getId() == currentId ) { - QFont f = a->font(); - f.setBold( true ); - a->setFont( f ); - } - a->setData( subWindow->getId() ); - connect( a, SIGNAL( triggered(bool) ), this, SLOT( onSynchronizeView(bool) ) ); - } - } - } - } - if ( anAction->menu()->actions().isEmpty() ) { - anAction->setData( 0 ); - anAction->menu()->addAction( tr( "MNU_SYNC_NO_VIEW" ) ); - } - } + SUIT_CameraProperties aProps = theView->cameraProperties(); + + Handle(V3d_View) aSourceView = getViewPort()->getView(); + + aSourceView->SetImmediateUpdate( Standard_False ); + + double anUpDir[3]; + double aPosition[3]; + double aFocalPoint[3]; + double aMapScaling; + + // get common properties + aProps.getFocalPoint(aFocalPoint[0], aFocalPoint[1], aFocalPoint[2]); + aProps.getPosition(aPosition[0], aPosition[1], aPosition[2]); + aProps.getViewUp(anUpDir[0], anUpDir[1], anUpDir[2]); + aMapScaling = aProps.getMappingScale() * 2.0; + + gp_Dir aProjDir(aPosition[0] - aFocalPoint[0], + aPosition[1] - aFocalPoint[1], + aPosition[2] - aFocalPoint[2]); + + // get custom view translation + Standard_Real aTranslation[3]; + aSourceView->At(aTranslation[0], aTranslation[1], aTranslation[2]); + + gp_Dir aLeftDir = gp_Dir(anUpDir[0], anUpDir[1], anUpDir[2]).Crossed( + gp_Dir(aProjDir.X(), aProjDir.Y(), aProjDir.Z())); + + // convert camera position into a view reference point + gp_Trsf aTrsf; + aTrsf.SetValues( aLeftDir.X(), anUpDir[0], aProjDir.X(), aTranslation[0], + aLeftDir.Y(), anUpDir[1], aProjDir.Y(), aTranslation[1], + aLeftDir.Z(), anUpDir[2], aProjDir.Z(), aTranslation[2], + Precision::Confusion(), + Precision::Confusion() ); + aTrsf.Invert(); + + // transform to view-space coordinate system + gp_Pnt aProjRef(aPosition[0], aPosition[1], aPosition[2]); + aProjRef.Transform(aTrsf); + + aSourceView->SetProj(aProjDir.X(), aProjDir.Y(), aProjDir.Z()); + + aSourceView->SetUp((Standard_Real)anUpDir[0], + (Standard_Real)anUpDir[1], + (Standard_Real)anUpDir[2]); + + // set panning + aSourceView->SetCenter(aProjRef.X(), aProjRef.Y()); + + // set mapping scale + Standard_Real aWidth, aHeight; + aSourceView->Size(aWidth, aHeight); + + if ( aWidth > aHeight ) + aSourceView->SetSize (aMapScaling * (aWidth / aHeight)); + else + aSourceView->SetSize (aMapScaling); + + aSourceView->Update(); + aSourceView->SetImmediateUpdate( Standard_True ); + + blockSignals( blocked ); } diff --git a/src/OCCViewer/OCCViewer_ViewWindow.h b/src/OCCViewer/OCCViewer_ViewWindow.h index 28d3d8d64..625e4c58c 100755 --- a/src/OCCViewer/OCCViewer_ViewWindow.h +++ b/src/OCCViewer/OCCViewer_ViewWindow.h @@ -195,6 +195,8 @@ public: virtual void updateViewAspects( const viewAspectList& ); virtual void clearViewAspects(); + virtual SUIT_CameraProperties cameraProperties(); + public slots: virtual void onFrontView(); virtual void onViewFitAll(); @@ -244,6 +246,9 @@ signals: void Hide( QHideEvent * ); void maximized( OCCViewer_ViewWindow*, bool ); +protected slots: + void synchronize( SUIT_ViewWindow* ); + public: virtual QImage dumpView(); virtual bool dumpViewToFormat( const QImage&, const QString& fileName, const QString& format ); @@ -315,13 +320,6 @@ protected: double myCurScale; -private slots: - void onSynchronizeView(bool); - void updateSyncViews(); - -private: - static void synchronizeView( OCCViewer_ViewWindow*, int ); - private: OCCViewer_ClippingDlg* myClippingDlg; QtxAction* myClippingAction; diff --git a/src/OCCViewer/resources/OCCViewer_images.ts b/src/OCCViewer/resources/OCCViewer_images.ts index 21c1ae705..40bb07511 100644 --- a/src/OCCViewer/resources/OCCViewer_images.ts +++ b/src/OCCViewer/resources/OCCViewer_images.ts @@ -123,9 +123,5 @@ ICON_OCCVIEWER_MINIMIZE occ_view_minimized.png - - ICON_OCCVIEWER_SYNC - occ_view_sync.png - diff --git a/src/OCCViewer/resources/OCCViewer_msg_en.ts b/src/OCCViewer/resources/OCCViewer_msg_en.ts index 301870645..029eda7dc 100644 --- a/src/OCCViewer/resources/OCCViewer_msg_en.ts +++ b/src/OCCViewer/resources/OCCViewer_msg_en.ts @@ -283,10 +283,6 @@ DSC_MINIMIZE_VIEW Minimize view - - DSC_SYNCHRONIZE_VIEW - Synchronize view - MNU_MAXIMIZE_VIEW Maximize @@ -295,14 +291,6 @@ MNU_MINIMIZE_VIEW Minimize - - MNU_SYNCHRONIZE_VIEW - Synchronize - - - MNU_SYNC_NO_VIEW - [ No appropriate view ] - OCCViewer_CreateRestoreViewDlg diff --git a/src/OCCViewer/resources/OCCViewer_msg_fr.ts b/src/OCCViewer/resources/OCCViewer_msg_fr.ts index 843d08b77..c54f67989 100755 --- a/src/OCCViewer/resources/OCCViewer_msg_fr.ts +++ b/src/OCCViewer/resources/OCCViewer_msg_fr.ts @@ -283,10 +283,6 @@ DSC_MINIMIZE_VIEW Minimiser la vue - - DSC_SYNCHRONIZE_VIEW - Synchroniser la vue - MNU_MAXIMIZE_VIEW Maximiser @@ -295,14 +291,6 @@ MNU_MINIMIZE_VIEW Minimiser - - MNU_SYNCHRONIZE_VIEW - Synchroniser - - - MNU_SYNC_NO_VIEW - [ Pas de vue appropriée ] - OCCViewer_CreateRestoreViewDlg diff --git a/src/SUIT/CMakeLists.txt b/src/SUIT/CMakeLists.txt index fcd2d6c29..e2f2c98e2 100644 --- a/src/SUIT/CMakeLists.txt +++ b/src/SUIT/CMakeLists.txt @@ -59,6 +59,7 @@ SET(GUI_HEADERS SUIT_ViewModel.h SUIT_ViewWindow.h SUIT_ShortcutMgr.h + SUIT_CameraProperties.h ) QT4_WRAP_CPP(GUI_HEADERS_MOC ${GUI_HEADERS}) @@ -93,6 +94,7 @@ SET(suit_SOURCES SUIT_ViewModel.cxx SUIT_ViewWindow.cxx SUIT_ShortcutMgr.cxx + SUIT_CameraProperties.cxx ) SET(GUITS_SOURCES resources/SUIT_msg_en.ts diff --git a/src/SUIT/Makefile.am b/src/SUIT/Makefile.am index 68f6184c0..e076f70e7 100755 --- a/src/SUIT/Makefile.am +++ b/src/SUIT/Makefile.am @@ -62,7 +62,8 @@ salomeinclude_HEADERS = \ SUIT_ViewManager.h \ SUIT_ViewModel.h \ SUIT_ViewWindow.h \ - SUIT_ShortcutMgr.h + SUIT_ShortcutMgr.h \ + SUIT_CameraProperties.h dist_libsuit_la_SOURCES = \ SUIT_Accel.cxx \ @@ -94,7 +95,8 @@ dist_libsuit_la_SOURCES = \ SUIT_ViewManager.cxx \ SUIT_ViewModel.cxx \ SUIT_ViewWindow.cxx \ - SUIT_ShortcutMgr.cxx + SUIT_ShortcutMgr.cxx \ + SUIT_CameraProperties.cxx MOC_FILES = \ SUIT_Accel_moc.cxx \ @@ -124,11 +126,11 @@ nodist_salomeres_DATA = \ SUIT_msg_en.qm \ SUIT_msg_fr.qm \ SUIT_images.qm - + dist_salomeres_DATA = \ resources/icon_visibility_on.png \ - resources/icon_visibility_off.png - + resources/icon_visibility_off.png \ + resources/view_sync.png libsuit_la_CPPFLAGS = $(QT_INCLUDES) -I$(srcdir)/../Qtx -I$(srcdir)/../ObjBrowser diff --git a/src/SUIT/SUIT_CameraProperties.cxx b/src/SUIT/SUIT_CameraProperties.cxx new file mode 100644 index 000000000..0eb7d9212 --- /dev/null +++ b/src/SUIT/SUIT_CameraProperties.cxx @@ -0,0 +1,374 @@ +// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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. +// +// 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 +// + +#include "SUIT_CameraProperties.h" + +/*! + \class SUIT_CameraProperties + \brief Base class for Camera Properties + + This class provides common properties that + can be used for any arbitrary camera. This + properties are employed by camera synchronization + algorithms. +*/ + +/*! + \brief Default constructor. + + Creates invalid camera properties data, i.e. IsValid() returns \c false. +*/ +SUIT_CameraProperties::SUIT_CameraProperties() +{ + // init with some default values + myUpDir[0] = 0.0; + myUpDir[1] = 1.0; + myUpDir[2] = 0.0; + + myPosition[0] = 0.0; + myPosition[1] = 0.0; + myPosition[2] = 500.0; + + myFocalPoint[0] = 0.0; + myFocalPoint[1] = 0.0; + myFocalPoint[2] = 0.0; + + myClippingRange[0] = 0.01; + myClippingRange[1] = 1000; + + myAxialScale[0] = 1.0; + myAxialScale[1] = 1.0; + myAxialScale[2] = 1.0; + + myMappingScale = 1000; + + myDimension = DimNone; // none dimension by default + myProjection = PrjOrthogonal; // orthogonal projection by default + myViewSide = ViewNone; // no side view by default +} + +/*! + \brief Destructor +*/ +SUIT_CameraProperties::~SUIT_CameraProperties() +{ +} + +/*! + \brief Check if camera properties are valid. + \return \c true if camera properties data is valid. + \sa setDimension() +*/ +bool SUIT_CameraProperties::isValid() const +{ + return myDimension != DimNone; +} + +/*! + \brief Check if this camera properties data is compatible with other camera properties. + \param other other properties data + \return \c true if both camera properties sets are compatible +*/ +bool SUIT_CameraProperties::isCompatible( const SUIT_CameraProperties& other ) +{ + bool result = false; + // check only valid data and data with same dimensions + if ( isValid() && other.isValid() && getDimension() == other.getDimension() ) { + switch( getDimension() ) { + case SUIT_CameraProperties::Dim2D: + // two 2d views are compatible if their view side is the same + result = getViewSide() == other.getViewSide(); + break; + case SUIT_CameraProperties::Dim3D: + // two 3d views are compatible if their projection mode is the same + result = getProjection() == other.getProjection(); + break; + default: + break; + } + } + return result; +} + +/*! + \brief get dimension supported by camera. + \return dimension mode. + \sa setDimension() +*/ +SUIT_CameraProperties::Dimension SUIT_CameraProperties::getDimension() const +{ + return myDimension; +} + +/*! + \brief set dimension supported by camera. + \param theDimension [in] dimension mode. + \sa getDimension() +*/ +void SUIT_CameraProperties::setDimension( const SUIT_CameraProperties::Dimension theDimension ) +{ + myDimension = theDimension; +} + +/*! + \brief get side view supported by camera (for 2d viewer). + + For 2d viewer, side view can be of following values: + - SUIT_CameraProperties::ViewNone - no side view (for instance, for true 2d viewer) + - SUIT_CameraProperties::ViewXY - XY side view of 3d scene + - SUIT_CameraProperties::ViewXZ - XZ side view of 3d scene + - SUIT_CameraProperties::ViewYZ - YZ side view of 3d scene + + \return side view. + \sa setViewSide() +*/ +SUIT_CameraProperties::ViewSide SUIT_CameraProperties::getViewSide() const +{ + return myViewSide; +} + +/*! + \brief set side view supported by camera (for 2d viewer). + + For 2d viewer, side view can be of following values: + - SUIT_CameraProperties::ViewNone - no side view (for instance, for true 2d viewer) + - SUIT_CameraProperties::ViewXY - XY side view of 3d scene + - SUIT_CameraProperties::ViewXZ - XZ side view of 3d scene + - SUIT_CameraProperties::ViewYZ - YZ side view of 3d scene + + \param theViewSide [in] view side. + \sa getViewSide() +*/ +void SUIT_CameraProperties::setViewSide( const SUIT_CameraProperties::ViewSide theViewSide ) +{ + myViewSide = theViewSide; +} + +/*! + \brief get projection mode supported by camera (for 3d viewer). + + For 3d viewer, projection mode can be of following values: + - SUIT_CameraProperties::PrjOrthogonal - orthogonal projection + - SUIT_CameraProperties::PrjPerspective - perspective projection + + \return projection mode. + \sa setProjection() +*/ +SUIT_CameraProperties::Projection SUIT_CameraProperties::getProjection() const +{ + return myProjection; +} + +/*! + \brief set projection mode supported by camera (for 3d viewer). + + For 3d viewer, projection mode can be of following values: + - SUIT_CameraProperties::PrjOrthogonal - orthogonal projection + - SUIT_CameraProperties::PrjPerspective - perspective projection + + \param theProjection [in] projection mode. + \sa getProjection() +*/ +void SUIT_CameraProperties::setProjection( const SUIT_CameraProperties::Projection theProjection ) +{ + myProjection = theProjection; +} + +/*! + \brief get camera up direction vector. + \param theX [out] vector's x coordinate in world-coordinates space. + \param theY [out] vector's y coordinate in world-coordinates space. + \param theZ [out] vector's z coordinate in world-coordinates space. + \sa setViewUp() +*/ +void SUIT_CameraProperties::getViewUp(double& theX, double& theY, double& theZ) const +{ + theX = myUpDir[0]; + theY = myUpDir[1]; + theZ = myUpDir[2]; +} + +/*! + \brief set camera up direction vector. + + It is recommended to set normalized vector coordinates for + synchronization compatibility. + + \param theX [in] vector's x coordinate in world-coordinates space. + \param theY [in] vector's y coordinate in world-coordinates space. + \param theZ [in] vector's z coordinate in world-coordinates space. + \sa getViewUp() +*/ +void SUIT_CameraProperties::setViewUp(const double theX, const double theY, const double theZ) +{ + myUpDir[0] = theX; + myUpDir[1] = theY; + myUpDir[2] = theZ; +} + +/*! + \brief get camera's position (eye). + \param theX [out] x coordinate in world-coordinates space. + \param theY [out] y coordinate in world-coordinates space. + \param theZ [out] z coordinate in world-coordinates space. + \sa setPosition() +*/ +void SUIT_CameraProperties::getPosition(double& theX, double& theY, double& theZ) const +{ + theX = myPosition[0]; + theY = myPosition[1]; + theZ = myPosition[2]; +} + +/*! + \brief get camera's position (eye). + \param theX [in] x coordinate in world-coordinates space. + \param theY [in] y coordinate in world-coordinates space. + \param theZ [in] z coordinate in world-coordinates space. + \sa getPosition() +*/ +void SUIT_CameraProperties::setPosition(const double theX, const double theY, const double theZ) +{ + myPosition[0] = theX; + myPosition[1] = theY; + myPosition[2] = theZ; +} + +/*! + \brief get camera's focal point (look point). + \param theX [out] x coordinate in world-coordinates space. + \param theY [out] y coordinate in world-coordinates space. + \param theZ [out] z coordinate in world-coordinates space. + \sa setFocalPoint() +*/ +void SUIT_CameraProperties::getFocalPoint(double& theX, double& theY, double& theZ) const +{ + theX = myFocalPoint[0]; + theY = myFocalPoint[1]; + theZ = myFocalPoint[2]; +} + +/*! + \brief set camera's focal point (look point). + \param theX [in] x coordinate in world-coordinates space. + \param theY [in] y coordinate in world-coordinates space. + \param theZ [in] z coordinate in world-coordinates space. + \sa getFocalPoint() +*/ +void SUIT_CameraProperties::setFocalPoint(const double theX, const double theY, const double theZ) +{ + myFocalPoint[0] = theX; + myFocalPoint[1] = theY; + myFocalPoint[2] = theZ; +} + +/*! + \brief get clipping range. + + The clipping range defines distances to near and far planes of + a clipping volume from a camera plane. + + The values should be higher than 0. + + \param theNear [out] distance to the near clipping plane. + \param theFar [out] distance to the far clipping plane. + \sa setClippingRange() +*/ +void SUIT_CameraProperties::getClippingRange(double& theNear, double& theFar) const +{ + theNear = myClippingRange[0]; + theFar = myClippingRange[1]; +} + +/*! + \brief set clipping range. + + The clipping range defines distances to near and far planes of + a clipping volume from a camera plane. + + The values should be higher than 0. + + \param theNear [in] distance to the near clipping plane. + \param theFar [in] distance to the far clipping plane. + \sa getClippingRange() +*/ +void SUIT_CameraProperties::setClippingRange(const double theNear, const double theFar) +{ + myClippingRange[0] = theNear; + myClippingRange[1] = theFar; +} + +/*! + \brief get window mapping scale (parallel scale). + + Mapping scale defines a mapping scaling factor for the height + of the viewport in world-coordinate distances. + + \return scaling value. + \sa setMappingScale() +*/ +double SUIT_CameraProperties::getMappingScale() const +{ + return myMappingScale; +} + +/*! + \brief set window mapping scale (parallel scale). + + Mapping scale defines a mapping scaling factor for the height + of the viewport in world-coordinate distances. + + \param theScale [in] the scaling. + \sa getMappingScale() +*/ +void SUIT_CameraProperties::setMappingScale(const double theScale) +{ + myMappingScale = theScale; +} + +/*! + \brief get scaling factors for world-coordinate space axes. + \param theScaleX [out] scale by x coordinate. + \param theScaleY [out] scale by y coordinate. + \param theScaleZ [out] scale by z coordinate. + \sa setAxialScale() +*/ +void SUIT_CameraProperties::getAxialScale(double& theScaleX, double& theScaleY, double& theScaleZ) +{ + theScaleX = myAxialScale[0]; + theScaleY = myAxialScale[1]; + theScaleZ = myAxialScale[2]; +} + +/*! + \brief set scaling factors for world-coordinate space axes. + \param theScaleX [in] scale by x coordinate. + \param theScaleY [in] scale by y coordinate. + \param theScaleZ [in] scale by z coordinate. + \sa getAxialScale() +*/ +void SUIT_CameraProperties::setAxialScale(const double theScaleX, const double theScaleY, const double theScaleZ) +{ + myAxialScale[0] = theScaleX; + myAxialScale[1] = theScaleY; + myAxialScale[2] = theScaleZ; +} diff --git a/src/SUIT/SUIT_CameraProperties.h b/src/SUIT/SUIT_CameraProperties.h new file mode 100644 index 000000000..532c39f06 --- /dev/null +++ b/src/SUIT/SUIT_CameraProperties.h @@ -0,0 +1,81 @@ +// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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. +// +// 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 +// + +#ifndef SUIT_CAMERAPROPERITES_H +#define SUIT_CAMERAPROPERITES_H + +#include "SUIT.h" + +class SUIT_EXPORT SUIT_CameraProperties +{ +public: + enum Dimension { DimNone, Dim2D, Dim3D }; + enum ViewSide { ViewNone, ViewXY, ViewXZ, ViewYZ }; + enum Projection { PrjOrthogonal, PrjPerspective }; + + SUIT_CameraProperties(); + virtual ~SUIT_CameraProperties(); + + bool isValid() const; + bool isCompatible( const SUIT_CameraProperties& other ); + + Dimension getDimension() const; + void setDimension( const Dimension theDimension ); + + ViewSide getViewSide() const; + void setViewSide( const ViewSide theViewSide ); + + Projection getProjection() const; + void setProjection( const Projection theProjection ); + + void getViewUp(double& theX, double& theY, double& theZ) const; + void setViewUp(const double theX, const double theY, const double theZ); + + void getPosition(double& theX, double& theY, double& theZ) const; + void setPosition(const double theX, const double theY, const double theZ); + + void getFocalPoint(double& theX, double& theY, double& theZ) const; + void setFocalPoint(const double theX, const double theY, const double theZ); + + void getClippingRange(double& theNear, double& theFar) const; + void setClippingRange(const double theNear, const double theFar); + + double getMappingScale() const; + void setMappingScale(const double theScale); + + void getAxialScale(double& theScaleX, double& theScaleY, double& theScaleZ); + void setAxialScale(const double theScaleX, const double theScaleY, const double theScaleZ); + +// common properties for all viewers +private: + Dimension myDimension; //!< dimension + ViewSide myViewSide; //!< side view (for 2d viewer) + Projection myProjection; //!< projection mode (for 3d viewer): orthogonal / perspective + double myUpDir[3]; //!< camera up vector + double myPosition[3]; //!< camera position (eye). + double myFocalPoint[3]; //!< focal point position in world-space coordinates + double myClippingRange[2]; //!< distance to front and back clipping planes + double myMappingScale; //!< window mapping scale (parallel projection scale) + double myAxialScale[3]; //!< scaling factors for world axes +}; + +#endif // SUIT_CAMERAPROPERITES_H diff --git a/src/SUIT/SUIT_Tools.cxx b/src/SUIT/SUIT_Tools.cxx index 32e9e1217..76544cda7 100755 --- a/src/SUIT/SUIT_Tools.cxx +++ b/src/SUIT/SUIT_Tools.cxx @@ -21,6 +21,9 @@ // #include "SUIT_Tools.h" +#include "SUIT_Application.h" +#include "SUIT_Desktop.h" +#include "SUIT_ViewWindow.h" #include @@ -88,3 +91,24 @@ void SUIT_Tools::centerWidget( QWidget* src, const QWidget* ref ) { SUIT_Tools::alignWidget( src, ref, Qt::AlignCenter ); } + +/*! + Get view windows - children of application \a app - + compatible to camera properties specified with \a props. +*/ +QList SUIT_Tools::compatibleViews( SUIT_Application* app, const SUIT_CameraProperties& props ) +{ + QList views; + if ( app && props.isValid() ) { + SUIT_Desktop* d = app->desktop(); + if ( d ) { + QList allViews = qFindChildren( d ); + foreach( SUIT_ViewWindow* view, allViews ) { + SUIT_CameraProperties otherProps = view->cameraProperties(); + if ( otherProps.isCompatible( props ) ) + views << view; + } + } + } + return views; +} diff --git a/src/SUIT/SUIT_Tools.h b/src/SUIT/SUIT_Tools.h index fbef3e06f..52e50326b 100755 --- a/src/SUIT/SUIT_Tools.h +++ b/src/SUIT/SUIT_Tools.h @@ -30,6 +30,11 @@ #include #include #include +#include + +class SUIT_Application; +class SUIT_CameraProperties; +class SUIT_ViewWindow; /*! \class SUIT_Tools @@ -45,6 +50,8 @@ public: static QFont stringToFont( const QString& fontDescription ); static void centerWidget( QWidget* src, const QWidget* ref ); + + static QList compatibleViews( SUIT_Application* app, const SUIT_CameraProperties& props ); }; #endif diff --git a/src/SUIT/SUIT_ViewWindow.cxx b/src/SUIT/SUIT_ViewWindow.cxx index 6bf0ebe3c..078c0d8f0 100755 --- a/src/SUIT/SUIT_ViewWindow.cxx +++ b/src/SUIT/SUIT_ViewWindow.cxx @@ -25,16 +25,19 @@ #include "SUIT_ViewWindow.h" #include "SUIT_Tools.h" +#include "SUIT_Session.h" #include "SUIT_Study.h" #include "SUIT_Desktop.h" #include "SUIT_MessageBox.h" #include "SUIT_Application.h" #include "SUIT_ViewManager.h" +#include "SUIT_ResourceMgr.h" #include "QtxActionToolMgr.h" #include "QtxMultiAction.h" #include #include +#include #include #include @@ -47,7 +50,7 @@ const int DUMP_EVENT = QEvent::User + 123; /*! Constructor.*/ SUIT_ViewWindow::SUIT_ViewWindow( SUIT_Desktop* theDesktop ) - : QMainWindow( theDesktop ), myManager( 0 ), myIsDropDown( true ) + : QMainWindow( theDesktop ), myManager( 0 ), myIsDropDown( true ), mySyncAction( 0 ) { myDesktop = theDesktop; @@ -326,3 +329,190 @@ int SUIT_ViewWindow::getId() const { return int(long(this)); } + +/*! + Get camera properties for the view window. + \return shared pointer on camera properties. Base implementation + returns null properties. +*/ +SUIT_CameraProperties SUIT_ViewWindow::cameraProperties() +{ + return SUIT_CameraProperties(); +} + +/*! + Synchronize this view window's camera properties with specified + view window. + + This method is a part of general views synchronization mechanism. + It should be redefined in successors. Base imlementation does nothing. + + \param otherWindow other view window +*/ +void SUIT_ViewWindow::synchronize( SUIT_ViewWindow* /*otherWindow*/ ) +{ + // base implementation does nothing +} + +/*! + Get action for views syncronization. + + This method is a part of general views synchronization mechanism. + It creates an action that can be inserted, for instance, to the toolbar. + + \return action for views synchronization +*/ +QAction* SUIT_ViewWindow::synchronizeAction() +{ + if ( !mySyncAction ) { + SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); + mySyncAction = new QtxAction( tr( "MNU_SYNCHRONIZE_VIEW" ), + resMgr->loadPixmap( "SUIT", tr( "ICON_VIEW_SYNC" ) ), + tr( "MNU_SYNCHRONIZE_VIEW" ), 0, this ); + mySyncAction->setStatusTip( tr( "DSC_SYNCHRONIZE_VIEW" ) ); + mySyncAction->setMenu( new QMenu( this ) ); + mySyncAction->setCheckable( true ); + connect( mySyncAction->menu(), SIGNAL( aboutToShow() ), this, SLOT( updateSyncViews() ) ); + connect( mySyncAction, SIGNAL( triggered( bool ) ), this, SLOT( onSynchronizeView( bool ) ) ); + } + return mySyncAction; +} + +/*! + Emit notification signal that the view is transformed. + Other views can use the signal for synchronization. +*/ +void SUIT_ViewWindow::emitViewModified() +{ + emit viewModified( this ); +} + +/*! + Update list of available view for the "Synchronize View" action +*/ +void SUIT_ViewWindow::updateSyncViews() +{ + QAction* anAction = synchronizeAction(); + if ( anAction && anAction->menu() ) { + int currentId = anAction->data().toInt(); + anAction->menu()->clear(); + SUIT_Application* app = SUIT_Session::session()->activeApplication(); + if ( app ) { + QList views = SUIT_Tools::compatibleViews( app, cameraProperties() ); + foreach ( SUIT_ViewWindow* view, views ) { + if ( !view || view == this ) continue; + QAction* a = anAction->menu()->addAction( view->windowTitle() ); + if ( view->getId() == currentId ) { + QFont f = a->font(); + f.setBold( true ); + a->setFont( f ); + } + a->setData( view->getId() ); + connect( a, SIGNAL( triggered( bool ) ), this, SLOT( onSynchronizeView( bool ) ) ); + } + } + if ( anAction->menu()->actions().isEmpty() ) { + anAction->setData( 0 ); + anAction->menu()->addAction( tr( "MNU_SYNC_NO_VIEW" ) ); + } + } +} + +/*! + "Synchronize View" action slot. +*/ +void SUIT_ViewWindow::onSynchronizeView( bool checked ) +{ + QAction* a = qobject_cast( sender() ); + if ( a ) { + synchronizeView( this, a->data().toInt() ); + } +} + +/*! + Synchronize camera properties of view \a viewWindow with + camera properties of view specified via \a id +*/ +void SUIT_ViewWindow::synchronizeView( SUIT_ViewWindow* viewWindow, int id ) +{ + SUIT_ViewWindow* sourceView = 0; + QList otherViews; + + bool isSync = viewWindow->synchronizeAction() && viewWindow->synchronizeAction()->isChecked(); + + int vwid = viewWindow->getId(); + + SUIT_Application* app = SUIT_Session::session()->activeApplication(); + if ( !app ) return; + SUIT_Desktop* d = app->desktop(); + if ( !d ) return; + + QList allViews = qFindChildren( d ); + foreach( SUIT_ViewWindow* vw, allViews ) { + if ( !vw->cameraProperties().isValid() ) + continue; // omit views not supporting camera properties + if ( vw->getId() == id ) + sourceView = vw; // remember source view + else if ( vw != viewWindow ) + otherViews.append( vw ); // collect all remaining views + } + + if ( isSync && id ) { + // remove all possible disconnections + foreach( SUIT_ViewWindow* vw, otherViews ) { + // disconnect target view + vw->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), viewWindow, SLOT( synchronize( SUIT_ViewWindow* ) ) ); + viewWindow->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), vw, SLOT( synchronize( SUIT_ViewWindow* ) ) ); + if ( sourceView ) { + // disconnect source view + vw->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), sourceView, SLOT( synchronize( SUIT_ViewWindow* ) ) ); + sourceView->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), vw, SLOT( synchronize( SUIT_ViewWindow* ) ) ); + } + QAction* a = vw->synchronizeAction(); + if ( a ) { + int anid = a->data().toInt(); + if ( a->isChecked() && ( anid == id || anid == vwid ) ) { + bool blocked = a->blockSignals( true ); + a->setChecked( false ); + a->blockSignals( blocked ); + } + } + } + if ( sourceView ) { + // reconnect source and target views + sourceView->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), viewWindow, SLOT( synchronize( SUIT_ViewWindow* ) ) ); + viewWindow->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), sourceView, SLOT( synchronize( SUIT_ViewWindow* ) ) ); + sourceView->connect( viewWindow, SIGNAL( viewModified( SUIT_ViewWindow* ) ), SLOT( synchronize( SUIT_ViewWindow* ) ) ); + viewWindow->connect( sourceView, SIGNAL( viewModified( SUIT_ViewWindow* ) ), SLOT( synchronize( SUIT_ViewWindow* ) ) ); + // synchronize target view with source view + viewWindow->synchronize( sourceView ); + if ( viewWindow->synchronizeAction() ) + viewWindow->synchronizeAction()->setData( sourceView->getId() ); + QAction* sourceAction = sourceView->synchronizeAction(); + if ( sourceAction ) { + sourceAction->setData( viewWindow->getId() ); + if ( !sourceAction->isChecked() ) { + bool blocked = sourceAction->blockSignals( true ); + sourceAction->setChecked( true ); + sourceAction->blockSignals( blocked ); + } + } + } + } + else if ( sourceView ) { + // reconnect source and target view + sourceView->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), viewWindow, SLOT( synchronize( SUIT_ViewWindow* ) ) ); + viewWindow->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), sourceView, SLOT( synchronize( SUIT_ViewWindow* ) ) ); + viewWindow->synchronize( sourceView ); + if ( viewWindow->synchronizeAction() ) + viewWindow->synchronizeAction() ->setData( sourceView->getId() ); + QAction* sourceAction = sourceView->synchronizeAction(); + if ( sourceAction ) { + if ( sourceAction->data().toInt() == viewWindow->getId() && sourceAction->isChecked() ) { + bool blocked = sourceAction->blockSignals( true ); + sourceAction->setChecked( false ); + sourceAction->blockSignals( blocked ); + } + } + } +} diff --git a/src/SUIT/SUIT_ViewWindow.h b/src/SUIT/SUIT_ViewWindow.h index e7dc87a1a..5b0789129 100755 --- a/src/SUIT/SUIT_ViewWindow.h +++ b/src/SUIT/SUIT_ViewWindow.h @@ -26,6 +26,7 @@ #define SUIT_VIEWWINDOW_H #include "SUIT.h" +#include "SUIT_CameraProperties.h" #include #include @@ -71,6 +72,9 @@ public: virtual void setDropDownButtons( bool ); bool dropDownButtons() const; + virtual SUIT_CameraProperties cameraProperties(); + virtual QAction* synchronizeAction(); + public slots: virtual void onDumpView(); @@ -85,7 +89,8 @@ signals: void keyPressed( SUIT_ViewWindow*, QKeyEvent* ); void keyReleased( SUIT_ViewWindow*, QKeyEvent* ); void contextMenuRequested( QContextMenuEvent *e ); - + void viewModified( SUIT_ViewWindow* ); + protected: void closeEvent( QCloseEvent* ); virtual void contextMenuEvent( QContextMenuEvent* ); @@ -93,15 +98,24 @@ protected: virtual bool action( const int ); virtual bool dumpViewToFormat( const QImage&, const QString& fileName, const QString& format ); + static void synchronizeView( SUIT_ViewWindow* viewWindow, int id ); + SUIT_Desktop* myDesktop; SUIT_ViewManager* myManager; +protected slots: + void updateSyncViews(); + void onSynchronizeView(bool); + virtual void synchronize( SUIT_ViewWindow* ); + void emitViewModified(); + private: typedef QMap< int, QList > ActionsMap; QtxActionToolMgr* myToolMgr; bool myIsDropDown; ActionsMap myMultiActions; + QAction* mySyncAction; }; #endif // SUIT_VIEWWINDOW_H diff --git a/src/SUIT/resources/SUIT_images.ts b/src/SUIT/resources/SUIT_images.ts index ff3f7d4fc..63c601050 100644 --- a/src/SUIT/resources/SUIT_images.ts +++ b/src/SUIT/resources/SUIT_images.ts @@ -11,5 +11,9 @@ ICON_DATAOBJ_INVISIBLE icon_visibility_off.png + + ICON_VIEW_SYNC + view_sync.png + diff --git a/src/SUIT/resources/SUIT_msg_en.ts b/src/SUIT/resources/SUIT_msg_en.ts index c1435da5b..31bbc382a 100644 --- a/src/SUIT/resources/SUIT_msg_en.ts +++ b/src/SUIT/resources/SUIT_msg_en.ts @@ -113,6 +113,18 @@ Do you want to overwrite it? NAME_COLUMN Name + + MNU_SYNCHRONIZE_VIEW + Synchronize + + + DSC_SYNCHRONIZE_VIEW + Synchronize view + + + MNU_SYNC_NO_VIEW + [ No appropriate view ] + SUIT_Study diff --git a/src/SUIT/resources/SUIT_msg_fr.ts b/src/SUIT/resources/SUIT_msg_fr.ts index 345b25512..d3785c28d 100755 --- a/src/SUIT/resources/SUIT_msg_fr.ts +++ b/src/SUIT/resources/SUIT_msg_fr.ts @@ -113,6 +113,18 @@ Voulez-vous l'écraser ? NAME_COLUMN Nom + + MNU_SYNCHRONIZE_VIEW + Synchroniser + + + DSC_SYNCHRONIZE_VIEW + Synchroniser la vue + + + MNU_SYNC_NO_VIEW + [ Pas de vue appropriée ] + SUIT_Study diff --git a/src/OCCViewer/resources/occ_view_sync.png b/src/SUIT/resources/view_sync.png old mode 100755 new mode 100644 similarity index 100% rename from src/OCCViewer/resources/occ_view_sync.png rename to src/SUIT/resources/view_sync.png diff --git a/src/SVTK/Makefile.am b/src/SVTK/Makefile.am index c048c3f49..1728d2de5 100755 --- a/src/SVTK/Makefile.am +++ b/src/SVTK/Makefile.am @@ -135,7 +135,6 @@ dist_salomeres_DATA=\ resources/vtk_view_recording_play.png \ resources/vtk_view_recording_pause.png \ resources/vtk_view_recording_stop.png \ - resources/vtk_view_sync.png \ resources/vtk_view_highlight.png nodist_salomeres_DATA = \ diff --git a/src/SVTK/SVTK_ViewWindow.cxx b/src/SVTK/SVTK_ViewWindow.cxx index 3a81fc72a..4be998ac7 100755 --- a/src/SVTK/SVTK_ViewWindow.cxx +++ b/src/SVTK/SVTK_ViewWindow.cxx @@ -76,7 +76,6 @@ #include "VTKViewer_Trihedron.h" #include "SVTK_View.h" -//#include "SVTK_MainWindow.h" #include "SVTK_Selector.h" #include "SVTK_Event.h" @@ -259,6 +258,8 @@ void SVTK_ViewWindow::Initialize(SVTK_View* theView, this,SIGNAL(contextMenuRequested(QContextMenuEvent *))); connect(theView,SIGNAL(selectionChanged()), theModel,SLOT(onSelectionChanged())); + + connect( this, SIGNAL( transformed( SVTK_ViewWindow* ) ), SLOT( emitViewModified() ) ); } /*! @@ -1022,6 +1023,7 @@ void SVTK_ViewWindow::onAdjustCubeAxes() void SVTK_ViewWindow::synchronize(SVTK_ViewWindow* otherViewWindow ) { + printf("SVTK_ViewWindow::synchronize(WRONG): %x\n", this); if ( otherViewWindow ) { bool blocked = blockSignals( true ); doSetVisualParameters( otherViewWindow->getVisualParameters(), true ); @@ -2065,15 +2067,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr) mgr->registerAction( anAction, ViewParametersId ); // Synchronize View - anAction = new QtxAction(tr("MNU_SYNCHRONIZE_VIEW"), - theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_SYNCHRONIZE" ) ), - tr( "MNU_SYNCHRONIZE_VIEW" ), 0, this); - anAction->setStatusTip(tr("DSC_SYNCHRONIZE_VIEW")); - anAction->setMenu( new QMenu( this ) ); - anAction->setCheckable(true); - connect(anAction->menu(), SIGNAL(aboutToShow()), this, SLOT(updateSyncViews())); - connect(anAction, SIGNAL(triggered(bool)), this, SLOT(onSynchronizeView(bool))); - mgr->registerAction( anAction, SynchronizeId ); + mgr->registerAction( synchronizeAction(), SynchronizeId ); // Switch between interaction styles anAction = new QtxAction(tr("MNU_SVTK_STYLE_SWITCH"), @@ -2361,134 +2355,6 @@ void SVTK_ViewWindow::hideEvent( QHideEvent * theEvent ) emit Hide( theEvent ); } -void SVTK_ViewWindow::synchronizeView( SVTK_ViewWindow* viewWindow, int id ) -{ - SVTK_ViewWindow* otherViewWindow = 0; - QList compatibleViews; - - bool isSync = viewWindow->toolMgr()->action( SynchronizeId )->isChecked(); - - int vwid = viewWindow->getId(); - - SUIT_Application* app = SUIT_Session::session()->activeApplication(); - if ( !app ) return; - - QList wmlist; - app->viewManagers( viewWindow->getViewManager()->getType(), wmlist ); - - foreach( SUIT_ViewManager* wm, wmlist ) { - QVector vwlist = wm->getViews(); - - foreach( SUIT_ViewWindow* vw, vwlist ) { - SVTK_ViewWindow* vtkVW = dynamic_cast( vw ); - if ( !vtkVW ) continue; - if ( vtkVW->getId() == id ) - otherViewWindow = vtkVW; - else if ( vtkVW != viewWindow ) - compatibleViews.append( vtkVW ); - } - } - - if ( isSync && id ) { - // remove all possible disconnections - foreach( SVTK_ViewWindow* vw, compatibleViews ) { - // disconnect target view - vw->disconnect( SIGNAL( transformed( SVTK_ViewPort* ) ), viewWindow, SLOT( synchronize( SVTK_ViewPort* ) ) ); - viewWindow->disconnect( SIGNAL( transformed( SVTK_ViewPort* ) ), vw, SLOT( synchronize( SVTK_ViewPort* ) ) ); - if ( otherViewWindow ) { - // disconnect source view - vw->disconnect( SIGNAL( transformed( SVTK_ViewPort* ) ), otherViewWindow, SLOT( synchronize( SVTK_ViewPort* ) ) ); - otherViewWindow->disconnect( SIGNAL( transformed( SVTK_ViewPort* ) ), vw, SLOT( synchronize( SVTK_ViewPort* ) ) ); - } - QAction* a = vw->toolMgr()->action( SynchronizeId ); - if ( a ) { - int anid = a->data().toInt(); - if ( a->isChecked() && ( anid == id || anid == vwid ) ) { - bool blocked = a->blockSignals( true ); - a->setChecked( false ); - a->blockSignals( blocked ); - } - } - } - if ( otherViewWindow ) { - // reconnect source and target view - otherViewWindow->disconnect( SIGNAL( transformed( SVTK_ViewWindow* ) ), viewWindow, SLOT( synchronize( SVTK_ViewWindow* ) ) ); - viewWindow->disconnect( SIGNAL( transformed( SVTK_ViewWindow* ) ), otherViewWindow, SLOT( synchronize( SVTK_ViewWindow* ) ) ); - otherViewWindow->connect( viewWindow, SIGNAL( transformed( SVTK_ViewWindow* ) ), SLOT( synchronize( SVTK_ViewWindow* ) ) ); - viewWindow->connect( otherViewWindow, SIGNAL( transformed( SVTK_ViewWindow* ) ), SLOT( synchronize( SVTK_ViewWindow* ) ) ); - // synchronize target view with source view - viewWindow->doSetVisualParameters( otherViewWindow->getVisualParameters(), true ); - viewWindow->toolMgr()->action( SynchronizeId )->setData( otherViewWindow->getId() ); - otherViewWindow->toolMgr()->action( SynchronizeId )->setData( viewWindow->getId() ); - if ( !otherViewWindow->toolMgr()->action( SynchronizeId )->isChecked() ) { - bool blocked = otherViewWindow->toolMgr()->action( SynchronizeId )->blockSignals( true ); - otherViewWindow->toolMgr()->action( SynchronizeId )->setChecked( true ); - otherViewWindow->toolMgr()->action( SynchronizeId )->blockSignals( blocked ); - } - } - } - else if ( otherViewWindow ) { - // reconnect source and target view - otherViewWindow->disconnect( SIGNAL( transformed( SVTK_ViewWindow* ) ), viewWindow, SLOT( synchronize( SVTK_ViewWindow* ) ) ); - viewWindow->disconnect( SIGNAL( transformed( SVTK_ViewWindow* ) ), otherViewWindow, SLOT( synchronize( SVTK_ViewWindow* ) ) ); - viewWindow->doSetVisualParameters( otherViewWindow->getVisualParameters(), true ); - viewWindow->toolMgr()->action( SynchronizeId )->setData( otherViewWindow->getId() ); - if ( otherViewWindow->toolMgr()->action( SynchronizeId )->data().toInt() == viewWindow->getId() && otherViewWindow->toolMgr()->action( SynchronizeId )->isChecked() ) { - bool blocked = otherViewWindow->toolMgr()->action( SynchronizeId )->blockSignals( true ); - otherViewWindow->toolMgr()->action( SynchronizeId )->setChecked( false ); - otherViewWindow->toolMgr()->action( SynchronizeId )->blockSignals( blocked ); - } - } -} - -/*! - "Synchronize View" action slot. -*/ -void SVTK_ViewWindow::onSynchronizeView(bool checked) -{ - QAction* a = qobject_cast( sender() ); - if ( a ) { - synchronizeView( this, a->data().toInt() ); - } -} - -/*! - Update list of available view for the "Synchronize View" action -*/ -void SVTK_ViewWindow::updateSyncViews() -{ - QAction* anAction = toolMgr()->action( SynchronizeId ); - if ( anAction && anAction->menu() ) { - int currentId = anAction->data().toInt(); - anAction->menu()->clear(); - SUIT_Application* app = SUIT_Session::session()->activeApplication(); - if ( app ) { - QList wmlist; - app->viewManagers( getViewManager()->getType(), wmlist ); - foreach( SUIT_ViewManager* wm, wmlist ) { - QVector vwlist = wm->getViews(); - foreach ( SUIT_ViewWindow* vw, vwlist ) { - SVTK_ViewWindow* vtkVW = dynamic_cast( vw ); - if ( !vtkVW || vtkVW == this ) continue; - QAction* a = anAction->menu()->addAction( vtkVW->windowTitle() ); - if ( vtkVW->getId() == currentId ) { - QFont f = a->font(); - f.setBold( true ); - a->setFont( f ); - } - a->setData( vtkVW->getId() ); - connect( a, SIGNAL( triggered(bool) ), this, SLOT( onSynchronizeView(bool) ) ); - } - } - } - if ( anAction->menu()->actions().isEmpty() ) { - anAction->setData( 0 ); - anAction->menu()->addAction( tr( "MNU_SYNC_NO_VIEW" ) ); - } - } -} - - /*! Emit transformed signal. */ @@ -2508,3 +2374,75 @@ void SVTK_ViewWindow::ProcessEvents(vtkObject* vtkNotUsed(theObject), if(self) self->emitTransformed(); } + +/*! + Get camera properties for the SVTK view window. + \return shared pointer on camera properties. +*/ +SUIT_CameraProperties SVTK_ViewWindow::cameraProperties() +{ + SUIT_CameraProperties aProps; + + // get vtk camera + vtkCamera* aCamera = getRenderer()->GetActiveCamera(); + if ( !aCamera ) + return aProps; + + aProps.setDimension( SUIT_CameraProperties::Dim3D ); + if ( toolMgr()->action( ParallelModeId ) ) { + if ( toolMgr()->action( ParallelModeId )->isChecked() ) + aProps.setProjection( SUIT_CameraProperties::PrjOrthogonal ); + else + aProps.setProjection( SUIT_CameraProperties::PrjPerspective ); + } + + double aFocalPoint[3]; + double aPosition[3]; + double aViewUp[3]; + + aCamera->OrthogonalizeViewUp(); + aCamera->GetFocalPoint(aFocalPoint); + aCamera->GetPosition(aPosition); + aCamera->GetViewUp(aViewUp); + + aProps.setFocalPoint(aFocalPoint[0], aFocalPoint[1], aFocalPoint[2]); + aProps.setPosition(aPosition[0], aPosition[1], aPosition[2]); + aProps.setViewUp(aViewUp[0], aViewUp[1], aViewUp[2]); + aProps.setMappingScale(aCamera->GetParallelScale()); + + return aProps; +} + +/*! + Synchronize views. + This implementation synchronizes camera propreties. +*/ +void SVTK_ViewWindow::synchronize( SUIT_ViewWindow* theView ) +{ + printf("SVTK_ViewWindow::synchronize: %x\n", this); + bool blocked = blockSignals( true ); + + SUIT_CameraProperties aProps = theView->cameraProperties(); + + // get camera + vtkCamera* aCamera = getRenderer()->GetActiveCamera(); + + double aFocalPoint[3]; + double aPosition[3]; + double aViewUp[3]; + + // get common properties + aProps.getViewUp(aViewUp[0], aViewUp[1], aViewUp[2]); + aProps.getPosition(aPosition[0], aPosition[1], aPosition[2]); + aProps.getFocalPoint(aFocalPoint[0], aFocalPoint[1], aFocalPoint[2]); + + // restore properties to the camera + aCamera->SetViewUp(aViewUp); + aCamera->SetPosition(aPosition); + aCamera->SetFocalPoint(aFocalPoint); + aCamera->SetParallelScale(aProps.getMappingScale()); + + Repaint(); + + blockSignals( blocked ); +} diff --git a/src/SVTK/SVTK_ViewWindow.h b/src/SVTK/SVTK_ViewWindow.h index 15c977e3e..abb6c0968 100755 --- a/src/SVTK/SVTK_ViewWindow.h +++ b/src/SVTK/SVTK_ViewWindow.h @@ -276,6 +276,8 @@ class SVTK_EXPORT SVTK_ViewWindow : public SUIT_ViewWindow //! To invoke a VTK event on #SVTK_RenderWindowInteractor instance void InvokeEvent(unsigned long theEvent, void* theCallData); + virtual SUIT_CameraProperties cameraProperties(); + signals: void Show( QShowEvent * ); void Hide( QHideEvent * ); @@ -372,6 +374,7 @@ public slots: virtual void synchronize(SVTK_ViewWindow*); protected slots: + void synchronize( SUIT_ViewWindow* ); void onKeyPressed(QKeyEvent* event); void onKeyReleased(QKeyEvent* event); void onMousePressed(QMouseEvent* event); @@ -446,13 +449,6 @@ protected: vtkPVAxesWidget* myAxesWidget; Qtx::BackgroundData myBackground; -private slots: - void onSynchronizeView(bool); - void updateSyncViews(); - -private: - static void synchronizeView( SVTK_ViewWindow*, int ); - private: QImage myDumpImage; }; diff --git a/src/SVTK/resources/SVTK_images.ts b/src/SVTK/resources/SVTK_images.ts index 362ffef6e..3794dfb54 100644 --- a/src/SVTK/resources/SVTK_images.ts +++ b/src/SVTK/resources/SVTK_images.ts @@ -55,10 +55,6 @@ ICON_SVTK_RECORDING_STOP vtk_view_recording_stop.png - - ICON_SVTK_SYNCHRONIZE - vtk_view_sync.png - ICON_SVTK_DYNAMIC_PRESLECTION_SWITCH vtk_view_highlight.png diff --git a/src/SVTK/resources/SVTK_msg_en.ts b/src/SVTK/resources/SVTK_msg_en.ts index f4da48aa4..623656fef 100644 --- a/src/SVTK/resources/SVTK_msg_en.ts +++ b/src/SVTK/resources/SVTK_msg_en.ts @@ -179,18 +179,6 @@ DSC_VIEWPARAMETERS_VIEW Change the parameters of the view - - MNU_SYNCHRONIZE_VIEW - Synchronize - - - DSC_SYNCHRONIZE_VIEW - Synchronize view - - - MNU_SYNC_NO_VIEW - [ No appropriate view ] - MNU_SVTK_PARALLEL_MODE Orthogonal Mode diff --git a/src/SVTK/resources/SVTK_msg_fr.ts b/src/SVTK/resources/SVTK_msg_fr.ts index 57c6e2f05..5c188b2f5 100755 --- a/src/SVTK/resources/SVTK_msg_fr.ts +++ b/src/SVTK/resources/SVTK_msg_fr.ts @@ -179,18 +179,6 @@ DSC_VIEWPARAMETERS_VIEW Changer les paramètres de la vue - - MNU_SYNCHRONIZE_VIEW - Synchroniser - - - DSC_SYNCHRONIZE_VIEW - Synchroniser la vue - - - MNU_SYNC_NO_VIEW - [ Pas de vue appropriée ] - MNU_SVTK_PARALLEL_MODE Mode orthogonal diff --git a/src/SVTK/resources/vtk_view_sync.png b/src/SVTK/resources/vtk_view_sync.png deleted file mode 100755 index 32b95228a8b97c791405a2b82d9a472aecf37c1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 627 zcmV-(0*w8MP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipl4 z3JE46$lxyk00Hw!L_t(I%axR|Yuiu|hrcs|q>D9Vh|v%-G{c)ab#!py(IJZ|9ZL-{ zT?!Gz7(=1zR7?UIv;-Fn?XJZ`2qaW<|A643OX;AxLnshQR>$Nc0wC)uoT|!)0R%9aLf=&G|T-TmeK{i{-N-Eru*LP^!6(1GJI z@L~WeWuM>gon^2xwZ5;YJ5CD4_f>WeR$BDr^*gqA_cC=UEdTDhV*{Yn7=Tfl&p9Cj zZtw2#`^Wc!l00mYUuV5)EjnBlTU$GGxxt?ua9)7tL8?&eU?ra{^Q+bY79bZFZ?j|+ z$Aw#+7Mgs`>}>N