Browse Source

Add ctkVTKThumbnailView

Julien Finet 14 years ago
parent
commit
21f0a81c20

+ 3 - 0
Libs/Visualization/VTK/Widgets/CMakeLists.txt

@@ -34,6 +34,8 @@ SET(KIT_SRCS
   ctkVTKSliceView_p.h
   ctkVTKTextPropertyWidget.cpp
   ctkVTKTextPropertyWidget.h
+  ctkVTKThumbnailView.cpp
+  ctkVTKThumbnailView.h
   )
 
 # Headers that should run through moc
@@ -46,6 +48,7 @@ SET(KIT_MOC_SRCS
   ctkVTKSliceView.h
   ctkVTKSliceView_p.h
   ctkVTKTextPropertyWidget.h
+  ctkVTKThumbnailView.h
   )
 
 # UI files

+ 2 - 0
Libs/Visualization/VTK/Widgets/Testing/Cpp/CMakeLists.txt

@@ -12,6 +12,7 @@ SET(TEST_SOURCES
   ctkTransferFunctionViewTest4.cpp
   ctkTransferFunctionViewTest5.cpp
   ctkVTKTextPropertyWidgetTest1.cpp
+  ctkVTKThumbnailViewTest1.cpp
   )
 
 IF(CTK_USE_CHARTS)
@@ -75,6 +76,7 @@ IF (CTK_USE_CHARTS)
   SIMPLE_TEST( ctkVTKScalarsToColorsWidgetTest1 )
 ENDIF(CTK_USE_CHARTS)
 SIMPLE_TEST( ctkVTKTextPropertyWidgetTest1 )
+SIMPLE_TEST( ctkVTKThumbnailViewTest1 )
 
 #
 # Add Tests expecting CTKData to be set

+ 93 - 0
Libs/Visualization/VTK/Widgets/Testing/Cpp/ctkVTKThumbnailViewTest1.cpp

@@ -0,0 +1,93 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+/*==============================================================================
+
+  Program: 3D Slicer
+
+  Copyright (c) 2010 Kitware Inc.
+
+  See Doc/copyright/copyright.txt
+  or http://www.slicer.org/copyright/copyright.txt for details.
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+  This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc.
+  and was partially funded by NIH grant 3P41RR013218-12S1
+
+==============================================================================*/
+
+// QT includes
+#include <QApplication>
+#include <QTimer>
+
+// qMRML includes
+#include "ctkVTKThumbnailView.h"
+
+// VTK includes
+#include <vtkSmartPointer.h>
+
+#include <vtkActor.h>
+#include <vtkCubeSource.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkRenderer.h>
+#include <vtkRenderWindow.h>
+#include <vtkRenderWindowInteractor.h>
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+int ctkVTKThumbnailViewTest1(int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+  
+  ctkVTKThumbnailView thumbnailView;
+  thumbnailView.setWindowTitle("Thumbnail view");
+  
+  ctkVTKRenderView renderView;
+  renderView.setWindowTitle("Render view");
+
+  vtkPolyDataMapper *mapper = vtkPolyDataMapper::New();
+  vtkCubeSource *cube= vtkCubeSource::New();
+  mapper->SetInput(cube->GetOutput());
+  cube->Delete();
+  vtkActor *actor = vtkActor::New();
+  actor->SetMapper(mapper);
+  mapper->Delete();
+
+  renderView.renderer()->AddActor(actor);
+  actor->Delete();
+
+  thumbnailView.setRendererToListen(renderView.renderer());
+
+  thumbnailView.show();
+  renderView.show();
+
+  if (argc < 2 || QString(argv[1]) != "-I" )
+    {
+    QTimer::singleShot(200, &app, SLOT(quit()));
+    }
+
+  return app.exec();
+}

+ 468 - 0
Libs/Visualization/VTK/Widgets/ctkVTKThumbnailView.cpp

@@ -0,0 +1,468 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+
+// CTK includes
+#include "ctkLogger.h"
+#include "ctkVTKThumbnailView.h"
+
+// VTK includes
+#include <vtkCamera.h>
+#include <vtkFollower.h>
+#include <vtkInteractorStyle.h>
+#include <vtkMath.h>
+#include <vtkOutlineSource.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkProperty.h>
+#include <vtkRenderWindow.h>
+#include <vtkRenderWindowInteractor.h>
+#include <vtkRenderer.h>
+#include <vtkSmartPointer.h>
+
+//--------------------------------------------------------------------------
+static ctkLogger logger("org.slicer.libs.qmrmlwidgets.ctkVTKThumbnailView");
+//--------------------------------------------------------------------------
+
+#define DEGREES2RADIANS 0.0174532925
+
+//-----------------------------------------------------------------------------
+class ctkVTKThumbnailViewPrivate
+{
+  Q_DECLARE_PUBLIC(ctkVTKThumbnailView);
+protected:
+  ctkVTKThumbnailView* const q_ptr;
+public:
+  ctkVTKThumbnailViewPrivate(ctkVTKThumbnailView& object);
+  ~ctkVTKThumbnailViewPrivate();
+  
+  void init();
+  void initCamera();
+  void updateBounds();
+  void updateCamera();
+  void resetCamera();
+
+  vtkRenderer*                       Renderer;
+  vtkRenderWindowInteractor*         Interactor;
+  
+  vtkOutlineSource*                  FOVBox;
+  vtkPolyDataMapper*                 FOVBoxMapper;
+  vtkFollower*                       FOVBoxActor;
+};
+
+//--------------------------------------------------------------------------
+// ctkVTKThumbnailViewPrivate methods
+
+//---------------------------------------------------------------------------
+ctkVTKThumbnailViewPrivate::ctkVTKThumbnailViewPrivate(ctkVTKThumbnailView& object)
+  : q_ptr(&object)
+{
+  this->Renderer = 0;
+  this->Interactor = 0;
+
+  this->FOVBox = 0;
+  this->FOVBoxMapper = 0;
+  this->FOVBoxActor = 0;
+}
+
+//---------------------------------------------------------------------------
+ctkVTKThumbnailViewPrivate::~ctkVTKThumbnailViewPrivate()
+{
+  this->FOVBox->Delete();
+  this->FOVBoxMapper->Delete();
+  this->FOVBoxActor->Delete();
+}
+
+//---------------------------------------------------------------------------
+void ctkVTKThumbnailViewPrivate::init()
+{
+  Q_Q(ctkVTKThumbnailView);
+
+  this->FOVBox = vtkOutlineSource::New();
+  this->FOVBoxMapper = vtkPolyDataMapper::New();
+  this->FOVBoxMapper->SetInput( this->FOVBox->GetOutput() );
+  this->FOVBoxActor = vtkFollower::New();
+
+  this->FOVBoxMapper->Update();
+   
+  this->FOVBoxActor->SetMapper( this->FOVBoxMapper );
+  this->FOVBoxActor->SetPickable(0);
+  this->FOVBoxActor->SetDragable(0);
+  this->FOVBoxActor->SetVisibility(1);
+  this->FOVBoxActor->SetScale(1.0, 1.0, 1.0);
+  this->FOVBoxActor->GetProperty()->SetColor( 0.1, 0.45, 0.1 );
+  this->FOVBoxActor->GetProperty()->SetLineWidth (2.0);
+
+  q->renderWindow()->GetInteractor()->SetInteractorStyle(0);
+  
+  // not sure what's for...
+  q->qvtkConnect(q->renderWindow(), vtkCommand::AbortCheckEvent,
+                 q, SLOT(checkAbort()));
+}
+
+//---------------------------------------------------------------------------
+void ctkVTKThumbnailViewPrivate::initCamera()
+{
+  Q_Q(ctkVTKThumbnailView);
+  vtkRenderer* ren = this->Renderer;
+  if (!ren)
+    {
+    return;
+    }
+  vtkCamera *cam = ren->IsActiveCameraCreated() ? ren->GetActiveCamera() : NULL;
+  if (!cam)
+    {
+    return;
+    }
+  vtkCamera *navcam = q->activeCamera();
+  if (!navcam)
+    {
+    return;
+    }
+  navcam->SetPosition ( cam->GetPosition() );
+  navcam->SetFocalPoint ( cam->GetFocalPoint() );
+  navcam->ComputeViewPlaneNormal();
+  navcam->SetViewUp( cam->GetViewUp() );
+  this->FOVBoxActor->SetCamera (navcam);
+  this->FOVBox->SetBoxTypeToOriented ( );
+}
+
+//---------------------------------------------------------------------------
+void ctkVTKThumbnailViewPrivate::updateBounds()
+{
+  Q_Q(ctkVTKThumbnailView);
+  vtkRenderer* ren = this->Renderer;
+  
+  vtkActorCollection *mainActors;
+  vtkActor *mainActor;
+  vtkActor *newActor;
+  vtkPolyDataMapper *newMapper;
+
+  // iterate thru the actor collection,
+  // remove item, delete actor, delete mapper.
+  vtkActorCollection *navActors = q->renderer()->GetActors();
+    
+  if (!navActors)
+    {
+    q->renderer()->RemoveAllViewProps();
+    navActors->RemoveAllItems();
+    }
+  if (!ren)
+    {
+    return;
+    }
+
+  double bounds[6];
+  double dimension;
+  double x,y,z;
+  double cutoff = 0.1;
+  double cutoffDimension;
+  
+  ren->ComputeVisiblePropBounds( bounds );
+  x = bounds[1] - bounds[0];
+  y = bounds[3] - bounds[2];
+  z = bounds[5] - bounds[4];
+  dimension = x*x + y*y + z*z;
+  cutoffDimension = cutoff * dimension;
+
+  // Get actor collection from the main viewer's renderer
+  mainActors = ren->GetActors();
+  if (!mainActors)
+    {
+    return;
+    }
+
+  // add the little FOV box to NavigationWidget's actors
+  q->renderer()->AddViewProp(this->FOVBoxActor);
+
+  for(mainActors->InitTraversal(); (mainActor = mainActors->GetNextActor()); )
+    {
+    // get the bbox of this actor
+    int vis = mainActor->GetVisibility();
+    //if (vis)
+    //  {
+      mainActor->GetBounds ( bounds );
+      // check to see if it's big enough to include in the scene...
+      x = bounds[1] - bounds[0];
+      y = bounds[3] - bounds[2];
+      z = bounds[5] - bounds[4];
+      dimension = x*x + y*y + z*z;
+     // }
+    // add a copy of the actor to the renderer
+    // only if it's big enough to count (don't bother with tiny
+    // and don't bother with invisible stuff)
+    if ( dimension > cutoffDimension && vis )
+      {
+      // ---new: create new actor, mapper, deep copy, add it.
+      newMapper = vtkPolyDataMapper::New();
+      newMapper->ShallowCopy (mainActor->GetMapper() );
+      newMapper->SetInput ( vtkPolyData::SafeDownCast(mainActor->GetMapper()->GetInput()) );
+
+      newActor = vtkActor::New();
+      newActor->ShallowCopy (mainActor );
+      newActor->SetMapper ( newMapper );
+      newMapper->Delete();
+      
+      q->renderer()->AddActor( newActor );
+      newActor->Delete();
+      }
+    }
+}
+
+//---------------------------------------------------------------------------
+void ctkVTKThumbnailViewPrivate::updateCamera()
+{
+  Q_Q(ctkVTKThumbnailView);
+   // Scale the FOVBox actor to show the
+  // MainViewer's window on the scene.
+  vtkRenderer *ren = this->Renderer;
+  vtkCamera *cam = ren ? (ren->IsActiveCameraCreated() ? ren->GetActiveCamera() : NULL) : NULL;
+  if (!cam)
+    {
+    return;
+    }
+  
+  // 3DViewer's camera configuration
+  double *focalPoint = cam->GetFocalPoint ( );
+  double *camPos= cam->GetPosition ( );
+  double *vpn = cam->GetViewPlaneNormal ();
+  double thetaV = (cam->GetViewAngle()) / 2.0;
+
+  // camera distance, and distance of FOVBox from focalPoint
+  double camDist = cam->GetDistance ();
+  double boxDist = camDist * 0.89;
+
+  // configure navcam based on main renderer's camera
+  vtkRenderer *navren = q->renderer();
+  vtkCamera *navcam = q->activeCamera();
+
+  if ( navcam == 0 )
+    {
+    return;
+    }
+  // give navcam the same parameters as MainViewer's ActiveCamera
+  navcam->SetPosition ( camPos );
+  navcam->SetFocalPoint ( focalPoint );
+  navcam->SetViewUp( cam->GetViewUp() );
+  navcam->ComputeViewPlaneNormal ( );
+
+  // compute FOVBox height & width to correspond 
+  // to the main viewer's size and aspect ratio, in world-coordinates,
+  // positioned just behind the near clipping plane, to make sure
+  // nothing in the scene occludes it.
+  double boxHalfHit;
+  if ( cam->GetParallelProjection() )
+    {
+    boxHalfHit = cam->GetParallelScale();
+    }
+  else
+    {
+    boxHalfHit = (camDist) * tan ( thetaV * DEGREES2RADIANS);
+    }
+
+  // 3D MainViewer height and width for computing aspect
+  int mainViewerWid = ren->GetRenderWindow()->GetSize()[0];
+  int mainViewerHit = ren->GetRenderWindow()->GetSize()[1];
+  // width of the FOVBox that represents MainViewer window.
+  double boxHalfWid = boxHalfHit * (double)mainViewerWid / (double)mainViewerHit;
+
+  // configure and position the FOVBox
+  double data [24];
+  data[0] = -1.0;
+  data[1] = -1.0;
+  data[2] = 0.0;
+  data[3] = 1.0;
+  data[4] = -1.0;
+  data[5] = 0.0;
+  data[6] = -1.0;
+  data[7] = 1.0;
+  data[8] = 0.0;
+  data[9] = 1.0;
+  data[10] = 1.0;
+  data[11] = 0.0;
+
+  data[12] = -1.0;
+  data[13] = -1.0;
+  data[14] = 0.0;
+  data[15] = 1.0;
+  data[16] = -1.0;
+  data[17] = 0.0;
+  data[18] = -1.0;
+  data[19] = 1.0;
+  data[20] = 0.0;
+  data[21] = 1.0;
+  data[22] = 1.0;
+  data[23] = 0.0;
+  this->FOVBox->SetCorners ( data );
+  // Position and scale FOVBox very close to camera,
+  // to prevent things in the scene from occluding it.
+  this->FOVBoxActor->SetScale ( boxHalfWid, boxHalfHit, 1.0);
+  this->FOVBoxActor->SetPosition (focalPoint[0]+ (vpn[0]*boxDist),
+                                  focalPoint[1] + (vpn[1]*boxDist),
+                                  focalPoint[2] + (vpn[2]*boxDist));
+  this->resetCamera();
+  // put the focal point back into the center of
+  // the scene without the FOVBox included,
+  // since ResetNavigationCamera moved it.
+  navcam->SetFocalPoint ( focalPoint );
+  navren->ResetCameraClippingRange();
+  navren->UpdateLightsGeometryToFollowCamera();
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKThumbnailViewPrivate::resetCamera()
+{
+  Q_Q(ctkVTKThumbnailView);
+  
+  vtkRenderer *ren = q->renderer();
+  vtkCamera *cam = q->activeCamera();
+  
+  if (!ren || !cam)
+    {
+    logger.error("Trying to reset non-existant camera");
+    return;
+    }
+  
+  double bounds[6];
+  ren->ComputeVisiblePropBounds( bounds );
+  
+  if (!vtkMath::AreBoundsInitialized(bounds))
+    {
+    logger.error("Cannot reset camera!");
+    return;
+    }
+  ren->InvokeEvent(vtkCommand::ResetCameraEvent, ren);
+
+  double vn[3];
+  cam->GetViewPlaneNormal(vn);
+
+  double center[3];
+  center[0] = (bounds[0] + bounds[1])/2.0;
+  center[1] = (bounds[2] + bounds[3])/2.0;
+  center[2] = (bounds[4] + bounds[5])/2.0;
+
+  double w1 = bounds[1] - bounds[0];
+  double w2 = bounds[3] - bounds[2];
+  double w3 = bounds[5] - bounds[4];
+  w1 *= w1;
+  w2 *= w2;
+  w3 *= w3;
+  double radius = w1 + w2 + w3;
+
+  // If we have just a single point, pick a radius of 1.0
+  radius = (radius==0)?(1.0):(radius);
+
+  // compute the radius of the enclosing sphere
+  radius = sqrt(radius)*0.5;
+
+  // default so that the bounding sphere fits within the view fustrum
+  // compute the distance from the intersection of the view frustum with the
+  // bounding sphere. Basically in 2D draw a circle representing the bounding
+  // sphere in 2D then draw a horizontal line going out from the center of
+  // the circle. That is the camera view. Then draw a line from the camera
+  // position to the point where it intersects the circle. (it will be tangent
+  // to the circle at this point, this is important, only go to the tangent
+  // point, do not draw all the way to the view plane). Then draw the radius
+  // from the tangent point to the center of the circle. You will note that
+  // this forms a right triangle with one side being the radius, another being
+  // the target distance for the camera, then just find the target dist using
+  // a sin.
+  double viewAngle = 20.0;
+  double distance = radius/sin(viewAngle*vtkMath::Pi()/360.0);
+
+  // check view-up vector against view plane normal
+  double* vup = cam->GetViewUp();
+  if ( fabs(vtkMath::Dot(vup,vn)) > 0.999 )
+    {
+    logger.warn("Resetting view-up since view plane normal is parallel");
+    cam->SetViewUp(-vup[2], vup[0], vup[1]);
+    }
+
+  // update the camera
+  cam->SetFocalPoint(center[0],center[1],center[2]);
+  cam->SetPosition(center[0]+distance*vn[0],
+                   center[1]+distance*vn[1],
+                   center[2]+distance*vn[2]);
+
+  ren->ResetCameraClippingRange( bounds );
+
+  // setup default parallel scale
+  cam->SetParallelScale(radius);
+}
+
+// --------------------------------------------------------------------------
+// ctkVTKThumbnailView methods
+
+// --------------------------------------------------------------------------
+ctkVTKThumbnailView::ctkVTKThumbnailView(QWidget* _parent) : Superclass(_parent)
+  , d_ptr(new ctkVTKThumbnailViewPrivate(*this))
+{
+  Q_D(ctkVTKThumbnailView);
+  d->init();
+
+  // Hide orientation widget
+  this->setOrientationWidgetVisible(false);
+}
+
+// --------------------------------------------------------------------------
+ctkVTKThumbnailView::~ctkVTKThumbnailView()
+{
+}
+
+//------------------------------------------------------------------------------
+void ctkVTKThumbnailView::setRendererToListen(vtkRenderer* newRenderer)
+{
+  Q_D(ctkVTKThumbnailView);
+  d->Renderer = newRenderer;
+  vtkRenderWindow* newRenderWindow = newRenderer->GetRenderWindow();
+  vtkRenderWindowInteractor* newInteractor = newRenderWindow->GetInteractor();
+  this->qvtkReconnect(d->Interactor, newInteractor,
+                      vtkCommand::EndInteractionEvent, this,SLOT(updateCamera()));
+  d->Interactor = newInteractor;
+  d->initCamera();
+  d->updateBounds();
+  d->updateCamera();
+}
+
+//---------------------------------------------------------------------------
+void ctkVTKThumbnailView::checkAbort()
+{
+  if (this->renderWindow()->GetEventPending())
+    {
+    this->renderWindow()->SetAbortRender(1);
+    }
+}
+
+//---------------------------------------------------------------------------
+void ctkVTKThumbnailView::updateCamera()
+{
+  Q_D(ctkVTKThumbnailView);
+  d->updateCamera();
+  this->scheduleRender();
+}
+
+//---------------------------------------------------------------------------
+void ctkVTKThumbnailView::updateBounds()
+{
+  Q_D(ctkVTKThumbnailView);
+  d->updateBounds();
+  this->scheduleRender();
+}

+ 59 - 0
Libs/Visualization/VTK/Widgets/ctkVTKThumbnailView.h

@@ -0,0 +1,59 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkVTKThumbnailView_h
+#define __ctkVTKThumbnailView_h
+
+// CTK includes
+#include "ctkVisualizationVTKWidgetsExport.h"
+#include "ctkVTKObject.h"
+#include "ctkVTKRenderView.h"
+
+class ctkVTKThumbnailViewPrivate;
+
+class CTK_VISUALIZATION_VTK_WIDGETS_EXPORT ctkVTKThumbnailView : public ctkVTKRenderView
+{
+  Q_OBJECT
+  QVTK_OBJECT
+
+public:
+  /// Superclass typedef
+  typedef ctkVTKRenderView Superclass;
+  
+  /// Constructors
+  explicit ctkVTKThumbnailView(QWidget* parent = 0);
+  virtual ~ctkVTKThumbnailView();
+
+  void setRendererToListen(vtkRenderer* renderer);
+
+protected slots:
+  void checkAbort();
+  void updateBounds();
+  void updateCamera();
+
+protected:
+  QScopedPointer<ctkVTKThumbnailViewPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkVTKThumbnailView);
+  Q_DISABLE_COPY(ctkVTKThumbnailView);
+};
+
+#endif