Fraxinus  17.12
An IGT application
cxCameraStyleForView.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 #include "cxCameraStyleForView.h"
34 
35 #include <vtkRenderer.h>
36 #include <vtkCamera.h>
37 #include <vtkLightCollection.h>
38 #include <vtkLight.h>
39 
40 #include "cxTrackingService.h"
41 #include "cxToolRep3D.h"
42 #include "cxView.h"
43 #include "boost/bind.hpp"
44 #include <vtkRenderWindow.h>
45 #include "vtkInteractorStyleUnicam.h"
46 #include "vtkInteractorStyleTrackballCamera.h"
47 #include "cxCoreServices.h"
48 #include "cxViewportListener.h"
49 
50 #include "cxTool.h"
51 #include <vtkRenderWindowInteractor.h>
52 #include "cxPatientModelService.h"
53 #include "cxRepContainer.h"
54 #include "cxLogger.h"
56 #include "cxNavigationAlgorithms.h"
57 #include "cxDoubleRange.h"
58 
59 namespace cx
60 {
61 
63 {
64  CX_LOG_CHANNEL_DEBUG("CA") << " info.focus="<<info.focus;
65  CX_LOG_CHANNEL_DEBUG("CA") << " info.pos="<<info.pos;
66  CX_LOG_CHANNEL_DEBUG("CA") << " info.vup="<<info.vup;
67  CX_LOG_CHANNEL_DEBUG("CA") << " info.vpn() ="<<info.vpn();
68  CX_LOG_CHANNEL_DEBUG("CA") << " info.distance() ="<<info.distance();
69 }
70 
71 
72 
74  mBlockCameraUpdate(false),
75  mBackend(backend)
76 {
77  mViewportListener.reset(new ViewportListener);
78  mViewportListener->setCallback(boost::bind(&CameraStyleForView::viewportChangedSlot, this));
79 
80  mPreRenderListener.reset(new ViewportPreRenderListener);
81  mPreRenderListener->setCallback(boost::bind(&CameraStyleForView::onPreRender, this));
82 
83  connect(mBackend->tracking().get(), &TrackingService::activeToolChanged,
84  this, &CameraStyleForView::activeToolChangedSlot);
85 }
86 
88 {
89  mViewportListener->stopListen();
90  mPreRenderListener->stopListen();
91 
92  this->disconnectTool();
93  mView = widget;
94  this->connectTool();
95 
96  mViewportListener->startListen(this->getRenderer());
97  mPreRenderListener->startListen(this->getRenderer());
98 }
99 
100 
101 ViewPtr CameraStyleForView::getView() const
102 {
103  return mView;
104 }
105 
106 void CameraStyleForView::viewportChangedSlot()
107 {
108  if (mBlockCameraUpdate)
109  return;
110  this->setModified();
111 }
112 
113 void CameraStyleForView::onPreRender()
114 {
115  this->applyCameraStyle();
116 }
117 
118 ToolRep3DPtr CameraStyleForView::getToolRep() const
119 {
120  if (!this->getView())
121  return ToolRep3DPtr();
122 
123  ToolRep3DPtr rep = RepContainer(this->getView()->getReps()).findFirst<ToolRep3D>(mFollowingTool);
124  return rep;
125 }
126 
127 vtkRendererPtr CameraStyleForView::getRenderer() const
128 {
129  if (!this->getView())
130  return vtkRendererPtr();
131  return this->getView()->getRenderer();
132 }
133 
134 vtkCameraPtr CameraStyleForView::getCamera() const
135 {
136  if (!this->getRenderer())
137  return vtkCameraPtr();
138  return this->getRenderer()->GetActiveCamera();
139 }
140 
141 void CameraStyleForView::setModified()
142 {
143  mPreRenderListener->setModified();
144 }
145 
146 void CameraStyleForView::applyCameraStyle()
147 {
148  vtkCameraPtr camera = this->getCamera();
149  if (!camera)
150  return;
151 
152  CameraInfo cam_old(camera);
153  CameraInfo cam_new = cam_old;
154 
155  cam_new.viewAngle = mStyle.mCameraViewAngle/M_PI*180;
156 
157  if (!mStyle.mFocusROI.isEmpty())
158  {
159  DoubleBoundingBox3D roi_r = this->getROI(mStyle.mFocusROI).getBox();
160  if (roi_r != DoubleBoundingBox3D::zero())
161  cam_new.focus = roi_r.center();
162  }
163 
164  if (mFollowingTool)
165  {
166  Transform3D rMto = this->get_rMto();
167 
168  // view up is relative to tool
169  cam_new.vup = rMto.vector(Vector3D(-1, 0, 0));
170 
171  if (mStyle.mFocusFollowTool)
172  {
173  // set focus to tool offset point
174  cam_new.focus = rMto.coord(Vector3D::Zero());
175  }
176 
177  if (mStyle.mCameraFollowTool)
178  {
179  // set camera on the tool line, keeping the previous distance from the focus.
180  Vector3D tooloffset = rMto.coord(Vector3D::Zero());
181  Vector3D e_tool = rMto.vector(Vector3D(0, 0, 1));
183  e_tool,
184  cam_old.distance(),
185  cam_new.focus);
186  }
187  }
188 
189  if (mStyle.mTableLock)
190  {
191  Vector3D table_up = mBackend->patient()->getOperatingTable().getVectorUp();
192  cam_new.vup = table_up;
193  }
194 
195  // reset vup based on vpn (do not change vpn after this point)
196  if (!similar(cam_new.vup, cam_old.vup))
197  {
198  cam_new.vup = NavigationAlgorithms::orthogonalize_vup(cam_new.vup, cam_new.vpn(), cam_old.vup);
199  }
200 
201  if (!mStyle.mAutoZoomROI.isEmpty())
202  {
203  cam_new = this->viewEntireAutoZoomROI(cam_new);
204  }
205 
206  if (mStyle.mCameraFollowTool)
207  {
208  cam_new.pos = NavigationAlgorithms::elevateCamera(mStyle.mElevation, cam_new.pos, cam_new.focus, cam_new.vup);
209  }
210 
211  this->updateCamera(cam_new);
212 }
213 
214 Transform3D CameraStyleForView::get_rMto()
215 {
216  Transform3D rMpr = mBackend->patient()->get_rMpr();
217  Transform3D prMt = mFollowingTool->get_prMt();
218  double offset = mFollowingTool->getTooltipOffset();
219  Transform3D tMto = createTransformTranslate(Vector3D(0, 0, offset));
220  Transform3D rMto = rMpr * prMt * tMto;
221  return rMto;
222 }
223 
224 Vector3D CameraStyleForView::getToolTip_r()
225 {
226  Transform3D rMto = this->get_rMto();
227  return rMto.coord(Vector3D::Zero());
228 }
229 
230 
231 CameraInfo CameraStyleForView::viewEntireAutoZoomROI(CameraInfo info)
232 {
233  CameraInfo retval = info;
234  if (mStyle.mAutoZoomROI.isEmpty())
235  return retval;
236 
237  RegionOfInterest roi_r = this->getROI(mStyle.mAutoZoomROI);
238  if (!roi_r.isValid())
239  return retval;
240 
241  double viewAngle = info.viewAngle/180.0*M_PI;
242 
243  this->getRenderer()->ComputeAspect();
244  double aspect[2];
245  this->getRenderer()->GetAspect(aspect);
246 
247  double viewAngle_vertical = viewAngle;
248  double viewAngle_horizontal = viewAngle*aspect[0];
249 
250  // move all calculations into a space p: (x,y,c)=(left,vup,focus)
251  // used to define a ROI bounding box aligned to the viewport.
252  Vector3D left = cross(info.vup, info.vpn());
253  Transform3D pMr = createTransformIJC(left, info.vup, info.focus).inv();
254  CameraInfo cam_proj;
255  cam_proj.focus = pMr.coord(info.focus);
256  cam_proj.vup = pMr.vector(info.vup);
257  cam_proj.pos = pMr.coord(info.pos);
258 
259  DoubleBoundingBox3D proj_bb = roi_r.getBox(pMr);
260 
261  Vector3D viewdirection = (cam_proj.focus - cam_proj.pos).normal();
262  cam_proj.pos = NavigationAlgorithms::findCameraPosByZoomingToROI(viewAngle_vertical,
263  viewAngle_horizontal,
264  cam_proj.focus,
265  cam_proj.vup,
266  cam_proj.vpn(),
267  proj_bb);
268  // keep focus at a const distance in front of the camera (if we dont do this, vpn might swap)
269  cam_proj.focus = cam_proj.pos + viewdirection * 100;
270 
271  cam_proj.pos = this->smoothZoomedCameraPosition(cam_proj.pos);
272 
273  retval.pos = pMr.inv().coord(cam_proj.pos);
274  retval.focus = pMr.inv().coord(cam_proj.focus);
275 
276  // experimental:
277  // IF inside bb: move pos to tool tip
278  // IF in front of bb: do nothing.
279  // IF in bbx2: interpolate between the two
280  // IF behind bb: keep pos inside bb
281  if (mStyle.mCameraLockToTooltip && mFollowingTool)
282  {
283  Transform3D rMto = this->get_rMto();
284  Vector3D proj_tool = (pMr*rMto).coord(Vector3D(0,0,0));
285  Vector3D e_z(0,0,1);
286  double bb_extension = 50; // distance from bb where we want to use only on-tool
287  double bb_extension_interpolate_interval = 50; // distance from bb where we want to interpolate between on-tool and off-tool
288 
289  bb_extension = 0; // distance from bb where we want to use only on-tool
290  bb_extension_interpolate_interval = 50; // distance from bb where we want to interpolate between on-tool and off-tool
291 
292  double tool_z = dot(proj_tool, e_z);
293  double bb_max_z = proj_bb[5];
294  double bb_ext_z = proj_bb[5] + bb_extension;
295 
296  RegionOfInterest notbehind_r = this->getROI(mStyle.mCameraNotBehindROI);
297  DoubleBoundingBox3D notbehind_proj = notbehind_r.getBox((pMr));
298  double notbehind = notbehind_proj[4];
299 
300  Vector3D new_pos;
301 
302  if (notbehind_r.isValid() && (tool_z < notbehind))
303  {
304  // behind roi: lock camera pos to closest pos inside bb
305  new_pos = proj_tool + (notbehind-tool_z)*e_z;
306 // CX_LOG_CHANNEL_DEBUG("CA") << " ** behind roi";
307  }
308  else
309  {
310  double s = (tool_z-bb_extension-bb_max_z)/bb_extension_interpolate_interval;
311  s = std::min(1.0, s);
312  s = std::max(0.0, s);
313  new_pos = (1.0-s)*proj_tool + (s)*cam_proj.pos;
314 // CX_LOG_CHANNEL_DEBUG("CA") << " ** roi pos= s=" << s;
315  }
316 
317  new_pos -= mStyle.mCameraTooltipOffset*e_z;
318 
319  // Move the camera onto the tool tip, keeping the distance vector constant.
320  // This gives the effect of _sitting on the tool tip_ while moving.
321  // Alternative: Dont change focal point, change view angle instead.
322  Vector3D delta = pMr.inv().coord(new_pos) - retval.pos;
323  retval.pos += delta;
324  retval.focus += delta;
325  }
326 
327  return retval;
328 }
329 
330 void CameraStyleForView::handleLights()
331 {
332  vtkRendererPtr renderer = this->getRenderer();
333 // CX_LOG_CHANNEL_DEBUG("CA") << "#lights: " << renderer->GetLights()->GetNumberOfItems();
334  renderer->GetLights()->InitTraversal();
335  vtkLight* light = renderer->GetLights()->GetNextItem();
336 // CX_LOG_CHANNEL_DEBUG("CA") << "light ";
337 // light->PrintSelf(std::cout, vtkIndent(2));
338 
339  // experiment: set a light to the left of the camera, pointing at focus
340  light->SetConeAngle(160);
341  light->SetLightTypeToCameraLight();
342  light->SetPosition(-0.5,0,1);
343 }
344 
349 Vector3D CameraStyleForView::smoothZoomedCameraPosition(Vector3D pos)
350 {
351  Vector3D filteredPos = pos;
352  filteredPos[2] = mZoomJitterFilter.newValue(pos[2]);
353  return filteredPos;
354 }
355 
356 RegionOfInterest CameraStyleForView::getROI(QString uid) const
357 {
358  DataPtr data = mBackend->patient()->getData(uid);
359  RegionOfInterestMetricPtr roi = boost::dynamic_pointer_cast<RegionOfInterestMetric>(data);
360  if (roi)
361  return roi->getROI();
362  return RegionOfInterest();
363 }
364 
365 void CameraStyleForView::activeToolChangedSlot()
366 {
367  ToolPtr newTool = mBackend->tracking()->getActiveTool();
368  if (newTool == mFollowingTool)
369  return;
370 
371  this->disconnectTool();
372  this->connectTool();
373 }
374 
375 bool CameraStyleForView::isToolFollowingStyle() const
376 {
377  return (mStyle.mCameraFollowTool || mStyle.mFocusFollowTool);
378 }
379 
380 void CameraStyleForView::connectTool()
381 {
382  if (!this->isToolFollowingStyle())
383  return;
384 
385  mFollowingTool = mBackend->tracking()->getActiveTool();
386 
387  if (!mFollowingTool)
388  return;
389 
390  if (!this->getView())
391  return;
392 
393  connect(mFollowingTool.get(), SIGNAL(toolTransformAndTimestamp(Transform3D, double)), this,
394  SLOT(setModified()));
395 
396  ToolRep3DPtr rep = this->getToolRep();
397  if (rep)
398  {
399  rep->setOffsetPointVisibleAtZeroOffset(false);
400  if (mStyle.mCameraFollowTool && fabs(mStyle.mElevation) < 0.01)
401  rep->setStayHiddenAfterVisible(true);
402  }
403 
404  this->setModified();
405 
406  report("Camera is following " + mFollowingTool->getName());
407 }
408 
409 void CameraStyleForView::disconnectTool()
410 {
411  if (mFollowingTool)
412  {
413  disconnect(mFollowingTool.get(), SIGNAL(toolTransformAndTimestamp(Transform3D, double)), this,
414  SLOT(setModified()));
415 
416  ToolRep3DPtr rep = this->getToolRep();
417  if (rep)
418  {
419  rep->setOffsetPointVisibleAtZeroOffset(true);
420  rep->setStayHiddenAfterVisible(false);
421  }
422 
423  mFollowingTool.reset();
424  }
425 }
426 
428 {
429  if (mStyle == style)
430  return;
431 
432  this->disconnectTool();
433 
434  if (style.mUniCam)
435  this->setInteractor(vtkInteractorStyleUnicamPtr::New());
436  else
437  this->setInteractor(vtkInteractorStyleTrackballCameraPtr::New());
438 
439  mStyle = style;
440 
441  this->connectTool();
442 }
443 
444 void CameraStyleForView::setInteractor(vtkSmartPointer<vtkInteractorStyle> style)
445 {
446  ViewPtr view = this->getView();
447  if (!view)
448  return;
449  vtkRenderWindowInteractor* interactor = view->getRenderWindow()->GetInteractor();
450  interactor->SetInteractorStyle(style);
451 }
452 
454 {
455  return mStyle;
456 }
457 
458 void CameraStyleForView::updateCamera(CameraInfo info)
459 {
460  vtkCameraPtr camera = this->getCamera();
461  if (!camera)
462  return;
463  CameraInfo cam_old(camera);
464 
465  // When viewing a scene of 10mm size, errors of 0.01mm are noticeable.
466  // Keep tolerance well below this level:
467  double tol = 0.001;
468  if (similar(cam_old, info, tol))
469  return; // break update loop: this event is triggered by camera change.
470 
471 // this->handleLights();
472 
473  mBlockCameraUpdate = true;
474  camera->SetPosition(info.pos.begin());
475  camera->SetFocalPoint(info.focus.begin());
476  camera->SetViewUp(info.vup.begin());
477  camera->SetViewAngle(info.viewAngle);
478  // use 2m, as the camera sometimes can move far from the object during zoom
479  camera->SetClippingRange(1, std::max<double>(2000, info.distance() * 10));
480  mBlockCameraUpdate = false;
481 }
482 
486 
488 {
489  currentValue = 0;
490  range = DoubleRange(0,0,0.1);
491 }
492 
493 double JitterFilter::newValue(double value)
494 {
495  // If outside range:
496  // reset interval and return value.
497  if (( value<=range.min() )||( value>=range.max() ))
498  {
499  double minimumInterval = 5.0;
500  double interval = std::min(minimumInterval, value/20);
501  double level = 0;
502 
503  if (value<range.min())
504  level = value + interval/2;
505  else if (value>range.max())
506  level = value - interval/2;
507 
508  range = DoubleRange(level-interval/2, level+interval/2, interval/10);
509 
510  currentValue = value;
511  }
512  return currentValue;
513 }
514 
518 
520 {
521  pos = Vector3D(camera->GetPosition());
522  focus = Vector3D(camera->GetFocalPoint());
523  vup = Vector3D(camera->GetViewUp());
524  viewAngle = camera->GetViewAngle();
525 }
526 
527 bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
528 {
529  return (similar(lhs.pos, rhs.pos, tol) &&
530  similar(lhs.focus, rhs.focus, tol) &&
531  similar(lhs.vup, rhs.vup, tol) &&
532  similar(lhs.viewAngle, rhs.viewAngle, tol)
533  );
534 }
535 
536 }//namespace cx
PlainObject normal() const
static Vector3D findCameraPosOnLineFixedDistanceFromFocus(Vector3D p_line, Vector3D e_line, double distance, Vector3D focus)
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
void setCameraStyle(CameraStyleData style)
Utility class for describing a bounded numeric range.
Definition: cxDoubleRange.h:53
boost::shared_ptr< class View > ViewPtr
DoubleBoundingBox3D getBox(Transform3D qMd=Transform3D::Identity())
Display a Tool in 3D.
Definition: cxToolRep3D.h:72
Listens to changes in viewport and camera matrix.
Vector3D cross(const Vector3D &a, const Vector3D &b)
compute cross product of a and b.
Definition: cxVector3D.cpp:62
boost::shared_ptr< class Data > DataPtr
void activeToolChanged(const QString &uId)
Transform3D createTransformIJC(const Vector3D &ivec, const Vector3D &jvec, const Vector3D &center)
vtkSmartPointer< class vtkRenderer > vtkRendererPtr
CameraStyleData getCameraStyle()
double newValue(double value)
static DoubleBoundingBox3D zero()
boost::shared_ptr< REP > findFirst(ToolPtr tool)
#define CX_LOG_CHANNEL_DEBUG(channel)
Definition: cxLogger.h:128
Transform3D createTransformTranslate(const Vector3D &translation)
void setView(ViewPtr widget)
double dot(const Vector3D &a, const Vector3D &b)
compute inner product (or dot product) of a and b.
Definition: cxVector3D.cpp:67
boost::shared_ptr< class RegionOfInterestMetric > RegionOfInterestMetricPtr
Representation of a floating-point bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
RegionOfInterest getROI() const
Vector3D center() const
void report(QString msg)
Definition: cxLogger.cpp:90
void debugPrint(CameraInfo info)
double distance() const
bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
boost::shared_ptr< class CoreServices > CoreServicesPtr
Definition: cxCameraStyle.h:59
Listens to the start render event in a vtkRenderer.
boost::shared_ptr< class ToolRep3D > ToolRep3DPtr
static Vector3D elevateCamera(double angle, Vector3D camera, Vector3D focus, Vector3D vup)
static Vector3D orthogonalize_vup(Vector3D vup, Vector3D vpn, Vector3D vup_fallback)
static Vector3D findCameraPosByZoomingToROI(double viewAngle_vertical, double viewAngle_horizontal, Vector3D focus, Vector3D vup, Vector3D vpn, const DoubleBoundingBox3D &bb)
CameraStyleForView(CoreServicesPtr backend)
#define M_PI
Vector3D vpn() const
vtkSmartPointer< class vtkCamera > vtkCameraPtr
Namespace for all CustusX production code.
boost::shared_ptr< class Tool > ToolPtr