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