Salome HOME
Bug on dragging rectangle in overview: Fatal error "ZFar should be greater than ZNear...
[modules/hydro.git] / src / HYDROGUI / HYDROGUI_Overview.cxx
1 // Copyright (C) 2014-2015  EDF-R&D
2 // This library is free software; you can redistribute it and/or
3 // modify it under the terms of the GNU Lesser General Public
4 // License as published by the Free Software Foundation; either
5 // version 2.1 of the License, or (at your option) any later version.
6 //
7 // This library is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10 // Lesser General Public License for more details.
11 //
12 // You should have received a copy of the GNU Lesser General Public
13 // License along with this library; if not, write to the Free Software
14 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
15 //
16 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
17 //
18
19 #include <HYDROGUI_Overview.h>
20 #include <OCCViewer_ViewPort3d.h>
21 #include <OCCViewer_ViewFrame.h>
22 #include <OCCViewer_ViewModel.h>
23 #include <OCCViewer_ViewManager.h>
24 #include <QtxRubberBand.h>
25 #include <QApplication>
26 #include <QPainter>
27 #include <QMouseEvent>
28 #include <QLayout>
29
30 //#define _DEVDEBUG_
31 #include "HYDRO_trace.hxx"
32
33 class HYDROGUI_OverviewBand : public QtxPolyRubberBand
34 {
35 public:
36   HYDROGUI_OverviewBand( HYDROGUI_Overview* theOverview );
37   virtual ~HYDROGUI_OverviewBand();
38
39   void initGeometry( const QPolygon& );
40   void update( bool isFromMain );
41   QPoint center() const;
42   int    count() const;
43
44   void   drag( const QPoint&, bool isStart );
45   bool   isDrag() const;
46   void   dragging( const QPoint& );
47   bool   contains( const QPoint& ) const;
48   QRect  boundingRect() const;
49
50 protected:
51   virtual void paintEvent( QPaintEvent* );
52
53 private:
54   HYDROGUI_Overview* myOverview;
55   bool               myIsDrag;
56   QPolygon           myStartPoints;
57   QPoint             myStartPnt;
58 };
59
60
61 HYDROGUI_OverviewBand::HYDROGUI_OverviewBand( HYDROGUI_Overview* theOverview )
62   : QtxPolyRubberBand( theOverview->getViewPort( false ) ),
63     myOverview( theOverview ), myIsDrag( false )
64 {
65 }
66
67 HYDROGUI_OverviewBand::~HYDROGUI_OverviewBand()
68 {
69 }
70
71 bool isEmpty( const QPolygon& thePoly )
72 {
73   QSize s = thePoly.boundingRect().size();
74   if( s.width() < 2 || s.height() < 2 )
75     return true;
76   else
77     return false;
78 }
79
80 void HYDROGUI_OverviewBand::initGeometry( const QPolygon& thePoly )
81 {
82   int w2 = myOverview->width()*2;
83   int h2 = myOverview->height()*2;
84
85   QPolygon aPoly;
86   for( int i=0, n=thePoly.size(); i<n; i++ )
87   {
88     QPoint p = thePoly.point( i );
89     if( p.x() < -w2 )
90       p.setX( -w2 );
91     if( p.x() > w2 )
92       p.setX( w2 );
93     if( p.y() < -h2 )
94       p.setY( -h2 );
95     if( p.y() > h2 )
96       p.setY( h2 );
97   }
98
99   QtxPolyRubberBand::initGeometry( thePoly );
100   if( isEmpty( thePoly ) )
101     hide();
102   else
103     show();
104 }
105
106 QRect HYDROGUI_OverviewBand::boundingRect() const
107 {
108   return myPoints.boundingRect();
109 }
110
111 QPoint HYDROGUI_OverviewBand::center() const
112 {
113   QPoint c;
114   int n = myPoints.size()-1;
115   if( n==0 )
116     return QPoint();
117
118   for( int i=0; i<n; i++ )
119     c += myPoints.point( i );
120
121   c = c/n;
122   return c;
123 }
124
125 void HYDROGUI_OverviewBand::drag( const QPoint& thePoint, bool isStart )
126 {
127   DEBTRACE("drag");
128   if( myIsDrag==isStart )
129     return;
130
131   if( isStart )
132   {
133     myStartPoints = myPoints;
134     /*if( contains( thePoint ) )
135       myStartPnt = thePoint;
136     else*/
137       myStartPnt = center();
138     myIsDrag = true;
139     dragging( thePoint );
140   }
141   else
142   {
143     dragging( thePoint );
144     myIsDrag = false;
145   }
146 }
147
148 bool HYDROGUI_OverviewBand::isDrag() const
149 {
150   return myIsDrag;
151 }
152
153 void HYDROGUI_OverviewBand::dragging( const QPoint& thePoint )
154 {
155   DEBTRACE("dragging");
156   int n = myPoints.size();
157   QPoint delta = thePoint - myStartPnt;
158   for( int i=0; i<n; i++ )
159     myPoints.setPoint( i, myStartPoints.point( i ) + delta );
160   initGeometry( myPoints );
161   update( false );
162 }
163
164 bool HYDROGUI_OverviewBand::contains( const QPoint& thePoint ) const
165 {
166   return myPoints.containsPoint( thePoint, Qt::OddEvenFill );
167 }
168
169 int HYDROGUI_OverviewBand::count() const
170 {
171   return myPoints.size();
172 }
173
174 void HYDROGUI_OverviewBand::update( bool isFromMain )
175 {
176   OCCViewer_ViewPort3d* main = myOverview->getViewPort( true );
177   if( isFromMain )
178   {
179     int w = main->width();
180     int h = main->height();
181
182     QPolygon poly;
183     QPoint p1 = myOverview->fromMain( 0, 0 );
184     QPoint p2 = myOverview->fromMain( w, 0 );
185     QPoint p3 = myOverview->fromMain( w, h );
186     QPoint p4 = myOverview->fromMain( 0, h );
187     poly.append( p1 );
188     poly.append( p2 );
189     poly.append( p3 );
190     poly.append( p4 );
191     poly.append( p1 );
192     initGeometry( poly );
193   }
194   else
195   {
196     OCCViewer_ViewPort3d* overview = myOverview->getViewPort( false );
197     QPoint c = center();
198     double x1, y1, z1, x2, y2, z2;
199     main->getView()->Convert( main->width()/2, main->height()/2, x1, y1, z1 );
200     DEBTRACE("x1, y1, z1 " << x1 << " " << y1 << " " << z1);
201     // Patch for OCCT 6.9.1, on 7.0.0 the moving of point to plane XY is not necessary
202 //    gp_Dir dm = main->getView()->Camera()->Direction();
203 //    double t1 = -z1/dm.Z();
204 //    x1 += dm.X()*t1;
205 //    y1 += dm.Y()*t1;
206 //    z1 += dm.Z()*t1;
207 //    DEBTRACE("x1, y1, z1 " << x1 << " " << y1 << " " << z1);
208
209     overview->getView()->Convert( c.x(), c.y(), x2, y2, z2 );
210     gp_Dir dov = overview->getView()->Camera()->Direction();
211     double t2 = -z2/dov.Z();
212     x2 += dov.X()*t2;
213     y2 += dov.Y()*t2;
214     z2 += dov.Z()*t2;
215     DEBTRACE("x2, y2, z2 " << x2 << " " << y2 << " " << z2);
216
217     gp_Trsf aTrsf;
218     aTrsf.SetTranslation( gp_Pnt( x1, y1, z1 ), gp_Pnt( x2, y2, z2 ) );
219
220     // Temporary patch for bug in OCCT 6.9.1
221 //    Handle(Graphic3d_Camera) cam = main->getView()->Camera();
222 //    gp_Dir u = cam->Up(), nu = u.Transformed (aTrsf);
223 //    gp_Pnt e = cam->Eye(), ne = e.Transformed (aTrsf);
224 //    gp_Pnt cen = cam->Center(), ncen = cen.Transformed (aTrsf);
225 //
226 //    if (!nu.IsEqual (u, 0.0))
227 //    {
228 //      cam->SetUp(nu);
229 //      DEBTRACE("nu " << nu.X() << " "  << nu.Y() << " "  << nu.Z());
230 //    }
231 //
232 //    if (!ne.IsEqual (e, 0.0))
233 //    {
234 //      cam->SetEye(ne);
235 //      DEBTRACE("ne " << ne.X() << " "  << ne.Y() << " "  << ne.Z())
236 //    }
237 //
238 //    if (!ncen.IsEqual(cen, 0.0))
239 //    {
240 //      cam->SetCenter (ncen);
241 //      DEBTRACE("ncen " << ncen.X() << " "  << ncen.Y() << " "  << ncen.Z())
242 //    }
243
244     //version for new OCCT:
245     main->getView()->Camera()->Transform( aTrsf );
246     main->repaint();
247   }
248 }
249
250 void HYDROGUI_OverviewBand::paintEvent( QPaintEvent* thePaintEvent )
251 {
252   QPainter painter( this );
253   painter.setRenderHint( QPainter::Antialiasing );
254
255   static QColor aColor = Qt::red;
256   static int aWidth = 2;
257   static QPen aPen( QBrush( aColor ), aWidth, Qt::DashDotLine );
258
259   painter.setPen( aPen );
260   painter.drawPolygon( myPoints );
261 }
262
263 //////////////
264
265 HYDROGUI_Overview::HYDROGUI_Overview( const QString& theTitle, int theMargin, QWidget* theParent )
266   : QFrame( theParent ), myMargin( theMargin ),
267     myMainView( 0 ), myViewPort( 0 ), myBand( 0 )
268 {
269   setWindowTitle( theTitle );
270   myLayout = new QGridLayout( this );
271   myLayout->setMargin( 0 );
272   myLayout->setSpacing( 0 );
273   myLayout->setRowStretch( 0, 1 );
274   myLayout->setColumnStretch( 0, 1 );
275 }
276
277 HYDROGUI_Overview::~HYDROGUI_Overview()
278 {
279   //delete myViewPort;
280 }
281
282 QImage HYDROGUI_Overview::dump() const
283 {
284   if( !myViewPort )
285     return QImage();
286
287   Handle(V3d_View) view = myViewPort->getView();
288   if( view.IsNull() )
289     return QImage();
290
291   int aWidth = myViewPort->width();
292   int aHeight = myViewPort->height();
293
294   Image_PixMap aPix;
295
296 #if OCC_VERSION_LARGE >= 0x07020000
297   view->ToPixMap( aPix,aWidth, aHeight,Graphic3d_BT_RGBA );
298   QImage anImage( aPix.Data(), aWidth, aHeight, QImage::Format_ARGB32 );
299   anImage = anImage.mirrored();
300   anImage = anImage.rgbSwapped();
301 #else
302   view->ToPixMap(aPix, aWidth, aHeight, Graphic3d_BT_RGB);
303   QImage anImage( aWidth, aHeight, QImage::Format_ARGB32 );
304   for ( int i = 0; i < aWidth; i++ ) {
305     for ( int j = 0; j < aHeight; j++ ) {
306       Quantity_Color pixel = aPix.PixelColor( i, j ).GetRGB();
307       QColor color = QColor::fromRgbF( pixel.Red(), pixel.Green(), pixel.Blue() );
308       anImage.setPixelColor( i, j, color );
309     }
310   }
311   if ( aPix.IsTopDown() )
312     anImage = anImage.mirrored();
313 #endif
314   if( myBand && myBand->isVisible() )
315   {
316     QPixmap aPixmap = QPixmap::fromImage( anImage );
317     QPoint p = myBand->boundingRect().topLeft();
318     myBand->render( &aPixmap, p );
319     anImage = aPixmap.toImage();
320   }
321   return anImage;
322 }
323
324 OCCViewer_ViewPort3d* HYDROGUI_Overview::getViewPort( bool isMain ) const
325 {
326   if ( isMain) 
327   {
328     if (myMainView!=NULL)
329       return myMainView->getViewPort();
330     else
331       return NULL;
332   }
333   else
334     return myViewPort;
335 }
336
337 void HYDROGUI_Overview::setMainView( OCCViewer_ViewFrame* theMainView )
338 {
339   myMainView = theMainView;
340   if( !myMainView )
341     return;
342
343   OCCViewer_ViewWindow* aMainView = myMainView->getView( OCCViewer_ViewFrame::MAIN_VIEW );
344   connect( aMainView, SIGNAL( vpTransformationFinished( OCCViewer_ViewWindow::OperationType ) ),
345            this,      SLOT( OnTransformationAfterOp( OCCViewer_ViewWindow::OperationType ) ) );
346   connect( aMainView->getViewPort(), SIGNAL( vpResizeEvent( QResizeEvent* ) ),
347            this,       SLOT( OnResizeEvent( QResizeEvent* ) ) );
348   connect( aMainView->getViewPort(), SIGNAL( vpTransformed( OCCViewer_ViewPort* ) ),
349            this,       SLOT( OnTransformation() ) ); 
350
351   connect( myMainView, SIGNAL(destroyed()),  this,  SLOT( onMainViewDestr() ) );
352
353   if( !myViewPort )
354   {
355     myViewPort = new OCCViewer_ViewPort3d( this, myMainView->getViewPort()->getViewer(), V3d_ORTHOGRAPHIC );
356
357     if( myViewPort )
358     {
359       myViewPort->setBackgroundColor( myMainView->getViewPort()->backgroundColor() );
360
361       connect( myViewPort, SIGNAL(destroyed()),  this,  SLOT( onViewPortDestr() ) );
362       connect( myViewPort, SIGNAL( vpMouseEvent( QMouseEvent* ) ), 
363               this,       SLOT( OnMouseEvent( QMouseEvent* ) ) );
364       connect( myViewPort, SIGNAL( vpResizeEvent( QResizeEvent* ) ),
365               this,       SLOT( OnResizeEvent( QResizeEvent* ) ) );
366
367       myLayout->addWidget( myViewPort, 0, 0 );
368     }
369   }
370
371 #if defined(TEST_MODE) || defined(_DEBUG)
372   //qApp->installEventFilter( this );
373 #endif
374
375   qApp->processEvents();
376
377   setTopView();
378
379   qApp->processEvents();
380
381   if( !myBand )
382     myBand = new HYDROGUI_OverviewBand( this );
383
384   myBand->update( true );
385 }
386
387 void HYDROGUI_Overview::setTopView()
388 {
389   if( !myViewPort )
390     return;
391
392   Handle(V3d_View) aView3d = myViewPort->getView();
393   if( !aView3d.IsNull() )
394     aView3d->SetProj( V3d_Zpos );
395   myViewPort->fitAll();
396
397   // Apply margins for internal area in the view port
398   if( myMargin>0 )
399   {
400     QRect aRect( -myMargin, -myMargin, myViewPort->width()+2*myMargin, myViewPort->height()+2*myMargin );
401     myViewPort->fitRect( aRect );
402   }
403
404   if( myBand )
405     myBand->update( true );
406 }
407
408 void HYDROGUI_Overview::OnTransformationAfterOp( OCCViewer_ViewWindow::OperationType theOp )
409 {
410   if( myViewPort && theOp>=OCCViewer_ViewWindow::WINDOWFIT )
411   {
412     myViewPort->fitAll();
413   }
414   if( theOp==OCCViewer_ViewWindow::FITSELECTION )
415   {
416     CustomFitSelection();
417   }
418   OnTransformation();
419 }
420
421 void HYDROGUI_Overview::OnTransformation()
422 {
423   if( myBand )
424     myBand->update( true );
425 }
426
427 QPoint HYDROGUI_Overview::fromMain( int xp, int yp ) const
428 {
429   if( !myMainView || !myViewPort || !myViewPort->isVisible() )
430     return QPoint();
431
432   const double EPS = 1E-2;
433
434   Handle(V3d_View) aMain = myMainView->getViewPort()->getView();
435   Handle(V3d_View) aXY = myViewPort->getView();
436
437   // View coordinates to 3d (world) coordinates
438   double x, y, z;
439   aMain->Convert( xp, yp, x, y, z );
440
441   // Moving to the XY plane
442   gp_Vec aDir = aMain->Camera()->Direction();
443   if( fabs( aDir.Z() )<EPS )
444     return QPoint();
445
446   double t = -z/aDir.Z();
447   x += t * aDir.X();
448   y += t * aDir.Y();
449   z += t * aDir.Z();
450
451   // 3d (world) coordinates to view coordinates in the overview
452   aXY->Convert( x, y, z, xp, yp );
453
454   return QPoint( xp, yp );
455 }
456
457 void HYDROGUI_Overview::OnMouseEvent( QMouseEvent* theEvent )
458 {
459   QPoint mp = theEvent->pos();
460   if( !myBand )
461     return;
462
463   switch( theEvent->type() )
464   {
465   case QEvent::MouseButtonPress:
466     myBand->drag( mp, true );
467     break;
468
469   case QEvent::MouseButtonRelease:
470     myBand->drag( mp, false );
471     break;
472
473   case QEvent::MouseMove:
474     if( myBand->isDrag() )
475       myBand->dragging( mp );
476     break;
477   }
478 }
479
480 bool HYDROGUI_Overview::eventFilter( QObject* theObject, QEvent* theEvent )
481 {
482 #if defined(TEST_MODE) || defined(_DEBUG)
483   /*switch( theEvent->type() )
484   {
485   case QEvent::MouseMove:
486     {
487       QPoint mp = ((QMouseEvent*)theEvent)->pos();
488       //mp = getViewPort(false)->mapFromGlobal(mp);
489       QString coords = QString( "(%0, %1)" ).arg( mp.x() ).arg( mp.y() );
490       std::string scoords = coords.toStdString();
491       qDebug( scoords.c_str() );
492     }
493     break;
494   }*/
495 #endif
496   return QFrame::eventFilter( theObject, theEvent );
497 }
498
499 void HYDROGUI_Overview::OnResizeEvent( QResizeEvent* )
500 {
501   if( myBand )
502     myBand->update( true );
503 }
504
505
506 void HYDROGUI_Overview::onMainViewDestr()
507 {
508   myMainView = NULL;
509   if (myViewPort == NULL)
510     return;
511   Handle(V3d_View) ov = myViewPort->getView();
512   ov->View()->Deactivate();
513   delete myViewPort; //this will delete myBand
514   myViewPort = NULL;
515   myBand = NULL;
516 }
517
518 void HYDROGUI_Overview::onViewPortDestr()
519 {
520   myViewPort = NULL;
521 }
522
523
524 void HYDROGUI_Overview::CustomFitSelection() const
525 {
526   DEBTRACE("CustomFitSelection");
527   OCCViewer_ViewPort3d* main = getViewPort( true );
528   if( !main )
529     return;
530
531   int w = main->width();
532   int h = main->height();
533
534   Bnd_Box bounding = BoundingForSelection();
535   if( bounding.IsVoid() )
536     return;
537
538   Standard_Real xmin, ymin, zmin, xmax, ymax, zmax;
539   bounding.Get( xmin, ymin, zmin, xmax, ymax, zmax );
540
541   QList<QPoint> points;
542   Standard_Integer xp, yp;
543   main->getView()->Convert( xmin, ymin, zmin, xp, yp ); points.append( QPoint( xp, yp ) );
544   main->getView()->Convert( xmax, ymin, zmin, xp, yp ); points.append( QPoint( xp, yp ) );
545   main->getView()->Convert( xmin, ymax, zmin, xp, yp ); points.append( QPoint( xp, yp ) );
546   main->getView()->Convert( xmax, ymax, zmin, xp, yp ); points.append( QPoint( xp, yp ) );
547   main->getView()->Convert( xmin, ymin, zmax, xp, yp ); points.append( QPoint( xp, yp ) );
548   main->getView()->Convert( xmax, ymin, zmax, xp, yp ); points.append( QPoint( xp, yp ) );
549   main->getView()->Convert( xmin, ymax, zmax, xp, yp ); points.append( QPoint( xp, yp ) );
550   main->getView()->Convert( xmax, ymax, zmax, xp, yp ); points.append( QPoint( xp, yp ) );
551
552   int xpmin, ypmin, xpmax, ypmax;
553   bool isFirst = true;
554   foreach( QPoint p, points )
555   {
556     int x = p.x(), y = p.y();
557     if( isFirst || x<xpmin )
558       xpmin = x;
559     if( isFirst || x>xpmax )
560       xpmax = x;
561     if( isFirst || y<ypmin )
562       ypmin = y;
563     if( isFirst || y>ypmax )
564       ypmax = y;
565
566     isFirst = false;
567   }
568
569   const int margin = 5;
570   QRect r( xpmin-margin, ypmin-margin, xpmax-xpmin+2*margin, ypmax-ypmin+2*margin );
571   main->fitRect( r );
572 }
573
574 Handle(AIS_InteractiveContext) HYDROGUI_Overview::context() const
575 {
576   if( myMainView )
577   {
578     SUIT_ViewModel* vm = myMainView->getViewManager()->getViewModel();
579     OCCViewer_Viewer* viewer = dynamic_cast<OCCViewer_Viewer*>( vm );
580     if( viewer )
581       return viewer->getAISContext();
582   }
583   return Handle(AIS_InteractiveContext)();
584 }
585
586 typedef NCollection_DataMap<Handle(SelectMgr_SelectableObject), Handle(SelectMgr_IndexedMapOfOwner)> AIS_MapOfObjectOwners1;
587   typedef NCollection_DataMap<Handle(SelectMgr_SelectableObject), Handle(SelectMgr_IndexedMapOfOwner)>::Iterator AIS_MapIteratorOfMapOfObjectOwners1;
588 Bnd_Box HYDROGUI_Overview::BoundingForSelection() const
589 {
590   DEBTRACE("BoundingForSelection");
591   Handle(AIS_InteractiveContext) c = context();
592
593   Bnd_Box aBndSelected;
594
595   AIS_MapOfObjectOwners1 anObjectOwnerMap;
596   for (c->InitSelected(); c->MoreSelected(); c->NextSelected())
597   {
598     const Handle(SelectMgr_EntityOwner)& anOwner = c->SelectedOwner();
599     Handle(AIS_InteractiveObject) anObj = Handle(AIS_InteractiveObject)::DownCast(anOwner->Selectable());
600     if (anObj->IsInfinite())
601     {
602       continue;
603     }
604
605     if (anOwner == anObj->GlobalSelOwner())
606     {
607       Bnd_Box aTmpBnd;
608       anObj->BoundingBox (aTmpBnd);
609       aBndSelected.Add (aTmpBnd);
610     }
611     else
612     {
613       Handle(SelectMgr_IndexedMapOfOwner) anOwnerMap;
614       if (!anObjectOwnerMap.Find (anOwner->Selectable(), anOwnerMap))
615       {
616         anOwnerMap = new SelectMgr_IndexedMapOfOwner();
617         anObjectOwnerMap.Bind (anOwner->Selectable(), anOwnerMap);
618       }
619
620       anOwnerMap->Add (anOwner);
621     }
622   }
623
624   for (AIS_MapIteratorOfMapOfObjectOwners1 anIter (anObjectOwnerMap); anIter.More(); anIter.Next())
625   {
626     const Handle(SelectMgr_SelectableObject) anObject = anIter.Key();
627     Bnd_Box aTmpBox = anObject->BndBoxOfSelected (anIter.ChangeValue());
628     aBndSelected.Add (aTmpBox);
629   }
630
631   anObjectOwnerMap.Clear();
632
633   return aBndSelected;
634 }