1 /*=========================================================================
2 
3   Library:   CTK
4 
5   Copyright (c) Kitware Inc.
6 
7   Licensed under the Apache License, Version 2.0 (the "License");
8   you may not use this file except in compliance with the License.
9   You may obtain a copy of the License at
10 
11       http://www.apache.org/licenses/LICENSE-2.0.txt
12 
13   Unless required by applicable law or agreed to in writing, software
14   distributed under the License is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18 
19 =========================================================================*/
20 
21 // Qt includes
22 #include <QDebug>
23 
24 // CTK includes
25 #include "ctkLogger.h"
26 #include "ctkVTKThumbnailView.h"
27 
28 // VTK includes
29 #include <vtkCamera.h>
30 #include <vtkFollower.h>
31 #include <vtkInteractorStyle.h>
32 #include <vtkMath.h>
33 #include <vtkOutlineSource.h>
34 #include <vtkPolyDataMapper.h>
35 #include <vtkProperty.h>
36 #include <vtkRenderWindow.h>
37 #include <vtkRenderWindowInteractor.h>
38 #include <vtkRenderer.h>
39 #include <vtkSmartPointer.h>
40 #include <vtkVersion.h>
41 #include <vtkWeakPointer.h>
42 
43 //--------------------------------------------------------------------------
44 static ctkLogger logger("org.slicer.libs.qmrmlwidgets.ctkVTKThumbnailView");
45 //--------------------------------------------------------------------------
46 
47 #define DEGREES2RADIANS 0.0174532925
48 
49 //-----------------------------------------------------------------------------
50 class ctkVTKThumbnailViewPrivate
51 {
52   Q_DECLARE_PUBLIC(ctkVTKThumbnailView);
53 protected:
54   ctkVTKThumbnailView* const q_ptr;
55 public:
56   ctkVTKThumbnailViewPrivate(ctkVTKThumbnailView& object);
57   ~ctkVTKThumbnailViewPrivate();
58 
59   void init();
60   void initCamera();
61   void updateBounds();
62   void updateCamera();
63   void resetCamera();
64 
65   vtkRenderer*                       Renderer;
66   vtkWeakPointer<vtkRenderWindowInteractor> Interactor;
67 
68   vtkOutlineSource*                  FOVBox;
69   vtkPolyDataMapper*                 FOVBoxMapper;
70   vtkFollower*                       FOVBoxActor;
71 };
72 
73 //--------------------------------------------------------------------------
74 // ctkVTKThumbnailViewPrivate methods
75 
76 //---------------------------------------------------------------------------
ctkVTKThumbnailViewPrivate(ctkVTKThumbnailView & object)77 ctkVTKThumbnailViewPrivate::ctkVTKThumbnailViewPrivate(ctkVTKThumbnailView& object)
78   : q_ptr(&object)
79 {
80   this->Renderer = 0;
81   this->Interactor = 0;
82 
83   this->FOVBox = 0;
84   this->FOVBoxMapper = 0;
85   this->FOVBoxActor = 0;
86 }
87 
88 //---------------------------------------------------------------------------
~ctkVTKThumbnailViewPrivate()89 ctkVTKThumbnailViewPrivate::~ctkVTKThumbnailViewPrivate()
90 {
91   this->FOVBox->Delete();
92   this->FOVBoxMapper->Delete();
93   this->FOVBoxActor->Delete();
94 }
95 
96 //---------------------------------------------------------------------------
init()97 void ctkVTKThumbnailViewPrivate::init()
98 {
99   Q_Q(ctkVTKThumbnailView);
100 
101   this->FOVBox = vtkOutlineSource::New();
102   this->FOVBoxMapper = vtkPolyDataMapper::New();
103 #if VTK_MAJOR_VERSION <= 5
104   this->FOVBoxMapper->SetInput( this->FOVBox->GetOutput() );
105 #else
106   this->FOVBoxMapper->SetInputConnection( this->FOVBox->GetOutputPort() );
107 #endif
108   this->FOVBoxActor = vtkFollower::New();
109 
110   this->FOVBoxMapper->Update();
111 
112   this->FOVBoxActor->SetMapper( this->FOVBoxMapper );
113   this->FOVBoxActor->SetPickable(0);
114   this->FOVBoxActor->SetDragable(0);
115   this->FOVBoxActor->SetVisibility(1);
116   this->FOVBoxActor->SetScale(1.0, 1.0, 1.0);
117   this->FOVBoxActor->GetProperty()->SetColor( 0.1, 0.45, 0.1 );
118   this->FOVBoxActor->GetProperty()->SetLineWidth (2.0);
119 
120   q->renderWindow()->GetInteractor()->SetInteractorStyle(0);
121 
122   // not sure what's for...
123   q->qvtkConnect(q->renderWindow(), vtkCommand::AbortCheckEvent,
124                  q, SLOT(checkAbort()));
125 }
126 
127 //---------------------------------------------------------------------------
initCamera()128 void ctkVTKThumbnailViewPrivate::initCamera()
129 {
130   Q_Q(ctkVTKThumbnailView);
131   vtkRenderer* ren = this->Renderer;
132   if (!ren)
133     {
134     return;
135     }
136   vtkCamera *cam = ren->IsActiveCameraCreated() ? ren->GetActiveCamera() : NULL;
137   if (!cam)
138     {
139     return;
140     }
141   vtkCamera *navcam = q->activeCamera();
142   if (!navcam)
143     {
144     return;
145     }
146   navcam->SetPosition ( cam->GetPosition() );
147   navcam->SetFocalPoint ( cam->GetFocalPoint() );
148   navcam->ComputeViewPlaneNormal();
149   navcam->SetViewUp( cam->GetViewUp() );
150   this->FOVBoxActor->SetCamera (navcam);
151   this->FOVBox->SetBoxTypeToOriented ( );
152 }
153 
154 //---------------------------------------------------------------------------
updateBounds()155 void ctkVTKThumbnailViewPrivate::updateBounds()
156 {
157   Q_Q(ctkVTKThumbnailView);
158   vtkRenderer* ren = this->Renderer;
159 
160   vtkActorCollection *mainActors;
161   vtkActor *mainActor;
162   vtkActor *newActor;
163   vtkPolyDataMapper *newMapper;
164 
165   // iterate thru the actor collection,
166   // remove item, delete actor, delete mapper.
167   vtkActorCollection *navActors = q->renderer()->GetActors();
168 
169   if (!navActors)
170     {
171     q->renderer()->RemoveAllViewProps();
172     navActors->RemoveAllItems();
173     }
174   if (!ren)
175     {
176     return;
177     }
178 
179   double bounds[6];
180   double dimension;
181   double x,y,z;
182   double cutoff = 0.1;
183   double cutoffDimension;
184 
185   ren->ComputeVisiblePropBounds( bounds );
186   x = bounds[1] - bounds[0];
187   y = bounds[3] - bounds[2];
188   z = bounds[5] - bounds[4];
189   dimension = x*x + y*y + z*z;
190   cutoffDimension = cutoff * dimension;
191 
192   // Get actor collection from the main viewer's renderer
193   mainActors = ren->GetActors();
194   if (!mainActors)
195     {
196     return;
197     }
198 
199   // add the little FOV box to NavigationWidget's actors
200   q->renderer()->AddViewProp(this->FOVBoxActor);
201 
202   for(mainActors->InitTraversal(); (mainActor = mainActors->GetNextActor()); )
203     {
204     // get the bbox of this actor
205     int vis = mainActor->GetVisibility();
206     //if (vis)
207     //  {
208       mainActor->GetBounds ( bounds );
209       // check to see if it's big enough to include in the scene...
210       x = bounds[1] - bounds[0];
211       y = bounds[3] - bounds[2];
212       z = bounds[5] - bounds[4];
213       dimension = x*x + y*y + z*z;
214      // }
215     // add a copy of the actor to the renderer
216     // only if it's big enough to count (don't bother with tiny
217     // and don't bother with invisible stuff)
218     if ( dimension > cutoffDimension && vis )
219       {
220       // ---new: create new actor, mapper, deep copy, add it.
221       newMapper = vtkPolyDataMapper::New();
222       newMapper->ShallowCopy (mainActor->GetMapper() );
223 #if VTK_MAJOR_VERSION <= 5
224       newMapper->SetInput ( vtkPolyData::SafeDownCast(mainActor->GetMapper()->GetInput()) );
225 #else
226       newMapper->SetInputData ( vtkPolyData::SafeDownCast(mainActor->GetMapper()->GetInput()) );
227 #endif
228 
229       newActor = vtkActor::New();
230       newActor->ShallowCopy (mainActor );
231       newActor->SetMapper ( newMapper );
232       newMapper->Delete();
233 
234       q->renderer()->AddActor( newActor );
235       newActor->Delete();
236       }
237     }
238 }
239 
240 //---------------------------------------------------------------------------
updateCamera()241 void ctkVTKThumbnailViewPrivate::updateCamera()
242 {
243   Q_Q(ctkVTKThumbnailView);
244    // Scale the FOVBox actor to show the
245   // MainViewer's window on the scene.
246   vtkRenderer *ren = this->Renderer;
247   vtkCamera *cam = ren ? (ren->IsActiveCameraCreated() ? ren->GetActiveCamera() : NULL) : NULL;
248   if (!cam)
249     {
250     return;
251     }
252 
253   // 3DViewer's camera configuration
254   double *focalPoint = cam->GetFocalPoint ( );
255   double *camPos= cam->GetPosition ( );
256   double *vpn = cam->GetViewPlaneNormal ();
257   double thetaV = (cam->GetViewAngle()) / 2.0;
258 
259   // camera distance, and distance of FOVBox from focalPoint
260   double camDist = cam->GetDistance ();
261   double boxDist = camDist * 0.89;
262 
263   // configure navcam based on main renderer's camera
264   vtkRenderer *navren = q->renderer();
265   vtkCamera *navcam = q->activeCamera();
266 
267   if ( navcam == 0 )
268     {
269     return;
270     }
271   // give navcam the same parameters as MainViewer's ActiveCamera
272   navcam->SetPosition ( camPos );
273   navcam->SetFocalPoint ( focalPoint );
274   navcam->SetViewUp( cam->GetViewUp() );
275   navcam->ComputeViewPlaneNormal ( );
276 
277   // compute FOVBox height & width to correspond
278   // to the main viewer's size and aspect ratio, in world-coordinates,
279   // positioned just behind the near clipping plane, to make sure
280   // nothing in the scene occludes it.
281   double boxHalfHit;
282   if ( cam->GetParallelProjection() )
283     {
284     boxHalfHit = cam->GetParallelScale();
285     }
286   else
287     {
288     boxHalfHit = (camDist) * tan ( thetaV * DEGREES2RADIANS);
289     }
290 
291   // 3D MainViewer height and width for computing aspect
292   int mainViewerWid = ren->GetRenderWindow()->GetSize()[0];
293   int mainViewerHit = ren->GetRenderWindow()->GetSize()[1];
294   // width of the FOVBox that represents MainViewer window.
295   double boxHalfWid = boxHalfHit * static_cast<double>(mainViewerWid)
296                        / static_cast<double>(mainViewerHit);
297 
298   // configure and position the FOVBox
299   double data [24];
300   data[0] = -1.0;
301   data[1] = -1.0;
302   data[2] = 0.0;
303   data[3] = 1.0;
304   data[4] = -1.0;
305   data[5] = 0.0;
306   data[6] = -1.0;
307   data[7] = 1.0;
308   data[8] = 0.0;
309   data[9] = 1.0;
310   data[10] = 1.0;
311   data[11] = 0.0;
312 
313   data[12] = -1.0;
314   data[13] = -1.0;
315   data[14] = 0.0;
316   data[15] = 1.0;
317   data[16] = -1.0;
318   data[17] = 0.0;
319   data[18] = -1.0;
320   data[19] = 1.0;
321   data[20] = 0.0;
322   data[21] = 1.0;
323   data[22] = 1.0;
324   data[23] = 0.0;
325   this->FOVBox->SetCorners ( data );
326   // Position and scale FOVBox very close to camera,
327   // to prevent things in the scene from occluding it.
328   this->FOVBoxActor->SetScale ( boxHalfWid, boxHalfHit, 1.0);
329   this->FOVBoxActor->SetPosition (focalPoint[0]+ (vpn[0]*boxDist),
330                                   focalPoint[1] + (vpn[1]*boxDist),
331                                   focalPoint[2] + (vpn[2]*boxDist));
332   this->resetCamera();
333   // put the focal point back into the center of
334   // the scene without the FOVBox included,
335   // since ResetNavigationCamera moved it.
336   navcam->SetFocalPoint ( focalPoint );
337   navren->ResetCameraClippingRange();
338   navren->UpdateLightsGeometryToFollowCamera();
339 }
340 
341 // ----------------------------------------------------------------------------
resetCamera()342 void ctkVTKThumbnailViewPrivate::resetCamera()
343 {
344   Q_Q(ctkVTKThumbnailView);
345 
346   vtkRenderer *ren = q->renderer();
347   vtkCamera *cam = q->activeCamera();
348 
349   if (!ren || !cam)
350     {
351     logger.error("Trying to reset non-existant camera");
352     return;
353     }
354 
355   double bounds[6];
356   ren->ComputeVisiblePropBounds( bounds );
357 
358   if (!vtkMath::AreBoundsInitialized(bounds))
359     {
360     logger.error("Cannot reset camera!");
361     return;
362     }
363   ren->InvokeEvent(vtkCommand::ResetCameraEvent, ren);
364 
365   double vn[3];
366   cam->GetViewPlaneNormal(vn);
367 
368   double center[3];
369   center[0] = (bounds[0] + bounds[1])/2.0;
370   center[1] = (bounds[2] + bounds[3])/2.0;
371   center[2] = (bounds[4] + bounds[5])/2.0;
372 
373   double w1 = bounds[1] - bounds[0];
374   double w2 = bounds[3] - bounds[2];
375   double w3 = bounds[5] - bounds[4];
376   w1 *= w1;
377   w2 *= w2;
378   w3 *= w3;
379   double radius = w1 + w2 + w3;
380 
381   // If we have just a single point, pick a radius of 1.0
382   radius = (radius==0)?(1.0):(radius);
383 
384   // compute the radius of the enclosing sphere
385   radius = sqrt(radius)*0.5;
386 
387   // default so that the bounding sphere fits within the view fustrum
388   // compute the distance from the intersection of the view frustum with the
389   // bounding sphere. Basically in 2D draw a circle representing the bounding
390   // sphere in 2D then draw a horizontal line going out from the center of
391   // the circle. That is the camera view. Then draw a line from the camera
392   // position to the point where it intersects the circle. (it will be tangent
393   // to the circle at this point, this is important, only go to the tangent
394   // point, do not draw all the way to the view plane). Then draw the radius
395   // from the tangent point to the center of the circle. You will note that
396   // this forms a right triangle with one side being the radius, another being
397   // the target distance for the camera, then just find the target dist using
398   // a sin.
399   double viewAngle = 20.0;
400   double distance = radius/sin(viewAngle*vtkMath::Pi()/360.0);
401 
402   // check view-up vector against view plane normal
403   double* vup = cam->GetViewUp();
404   if ( fabs(vtkMath::Dot(vup,vn)) > 0.999 )
405     {
406     logger.warn("Resetting view-up since view plane normal is parallel");
407     cam->SetViewUp(-vup[2], vup[0], vup[1]);
408     }
409 
410   // update the camera
411   cam->SetFocalPoint(center[0],center[1],center[2]);
412   cam->SetPosition(center[0]+distance*vn[0],
413                    center[1]+distance*vn[1],
414                    center[2]+distance*vn[2]);
415 
416   ren->ResetCameraClippingRange( bounds );
417 
418   // setup default parallel scale
419   cam->SetParallelScale(radius);
420 }
421 
422 // --------------------------------------------------------------------------
423 // ctkVTKThumbnailView methods
424 
425 // --------------------------------------------------------------------------
ctkVTKThumbnailView(QWidget * _parent)426 ctkVTKThumbnailView::ctkVTKThumbnailView(QWidget* _parent) : Superclass(_parent)
427   , d_ptr(new ctkVTKThumbnailViewPrivate(*this))
428 {
429   Q_D(ctkVTKThumbnailView);
430   d->init();
431 
432   // Hide orientation widget
433   this->setOrientationWidgetVisible(false);
434 }
435 
436 // --------------------------------------------------------------------------
~ctkVTKThumbnailView()437 ctkVTKThumbnailView::~ctkVTKThumbnailView()
438 {
439 }
440 
441 //------------------------------------------------------------------------------
setRendererToListen(vtkRenderer * newRenderer)442 void ctkVTKThumbnailView::setRendererToListen(vtkRenderer* newRenderer)
443 {
444   Q_D(ctkVTKThumbnailView);
445   d->Renderer = newRenderer;
446   vtkRenderWindow* newRenderWindow = newRenderer ? newRenderer->GetRenderWindow() : 0;
447   vtkRenderWindowInteractor* newInteractor = newRenderWindow ? newRenderWindow->GetInteractor() : 0;
448   this->qvtkReconnect(d->Interactor, newInteractor,
449                       vtkCommand::EndInteractionEvent, this,SLOT(updateCamera()));
450   d->Interactor = newInteractor;
451   d->initCamera();
452   d->updateBounds();
453   d->updateCamera();
454 }
455 
456 //---------------------------------------------------------------------------
checkAbort()457 void ctkVTKThumbnailView::checkAbort()
458 {
459   if (this->renderWindow()->GetEventPending())
460     {
461     this->renderWindow()->SetAbortRender(1);
462     }
463 }
464 
465 //---------------------------------------------------------------------------
updateCamera()466 void ctkVTKThumbnailView::updateCamera()
467 {
468   Q_D(ctkVTKThumbnailView);
469   d->updateCamera();
470   this->scheduleRender();
471 }
472 
473 //---------------------------------------------------------------------------
updateBounds()474 void ctkVTKThumbnailView::updateBounds()
475 {
476   Q_D(ctkVTKThumbnailView);
477   d->updateBounds();
478   this->scheduleRender();
479 }
480