123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- /*=========================================================================
- 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.apache.org/licenses/LICENSE-2.0.txt
- 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>
- #include <vtkVersion.h>
- #include <vtkWeakPointer.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;
- vtkWeakPointer<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();
- #if VTK_MAJOR_VERSION <= 5
- this->FOVBoxMapper->SetInput( this->FOVBox->GetOutput() );
- #else
- this->FOVBoxMapper->SetInputConnection( this->FOVBox->GetOutputPort() );
- #endif
- 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() );
- #if VTK_MAJOR_VERSION <= 5
- newMapper->SetInput ( vtkPolyData::SafeDownCast(mainActor->GetMapper()->GetInput()) );
- #else
- newMapper->SetInputData ( vtkPolyData::SafeDownCast(mainActor->GetMapper()->GetInput()) );
- #endif
- 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 * static_cast<double>(mainViewerWid)
- / static_cast<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 ? newRenderer->GetRenderWindow() : 0;
- vtkRenderWindowInteractor* newInteractor = newRenderWindow ? newRenderWindow->GetInteractor() : 0;
- 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();
- }
|