# Rotation of camera in 3D view without random tilting

**URL:** https://discourse.slicer.org/t/rotation-of-camera-in-3d-view-without-random-tilting/31842
**Category:** Development
**Created:** [September 22, 2023, 10:20am UTC](https://discourse.slicer.org/t/rotation-of-camera-in-3d-view-without-random-tilting/31842 "2023-09-22T10:20:46Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![moraleda](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/moraleda/32/77356_2.png) [@moraleda](https://discourse.slicer.org/u/moraleda)
#### Post date: [September 22, 2023, 10:20am UTC](https://discourse.slicer.org/t/rotation-of-camera-in-3d-view-without-random-tilting/31842/1 "2023-09-22T10:20:46Z")

</div>

Hi everyone,

as some of you might feel as I do that the camera in 3D view tilts when rotating, I have rewritten the code in `Slicer/Libs/MRML/DisplayableManger/vtkMRMLCameraWidget.cxx` in `vtkMRMLCameraWidget::ProcessRotate` as the following and then built my own Slicer.

```auto
//----------------------------------------------------------------------------
bool vtkMRMLCameraWidget::ProcessRotate(vtkMRMLInteractionEventData* eventData)
{
  if (!this->Renderer || !eventData)
    {
    return false;
    }

  const int* eventPosition = eventData->GetDisplayPosition();
  int dx = eventPosition[0] - this->PreviousEventPosition[0];
  int dy = eventPosition[1] - this->PreviousEventPosition[1];
  if (dx == 0 && dy == 0)
    {
    return true;
    }

  const int *size = this->Renderer->GetRenderWindow()->GetSize();

  double delta_elevation = -20.0 / size[1];
  double delta_azimuth = -20.0 / size[0];

  double rxf = (double)dx * delta_azimuth * this->MotionFactor;
  double ryf = (double)dy * delta_elevation * this->MotionFactor;

  vtkCamera* camera = this->GetCamera();
  if (!camera)
    {
    return false;
    }

  bool wasCameraNodeModified = this->CameraModifyStart();

  if (this->CameraTiltLocked == true)
    {
    camera->Azimuth(rxf);
    }
  else
    {
    const double* cameraPosition = camera->GetPosition();
    const double* cameraFocalPoint = camera->GetFocalPoint();
    const double* cameraViewUp = camera->GetViewUp();
    bool upside_down = cameraViewUp[2] < 0;
    double upside_down_factor = upside_down ? -1.0 : 1.0;

    double P[3] = {
        cameraPosition[0] - cameraFocalPoint[0],
        cameraPosition[1] - cameraFocalPoint[1],
        cameraPosition[2] - cameraFocalPoint[2]
    };

    double H = sqrt(P[0] * P[0] + P[1] * P[1]);
    double elev = atan2(P[2], H);

    double sin_elev = sin(elev);
    double azi;
    if (abs(sin_elev) < 0.8) {
      azi = atan2(P[1], P[0]);
    } else {
      if (sin_elev < -0.8) {
          azi = atan2(upside_down_factor * cameraViewUp[1], upside_down_factor * cameraViewUp[0]);
      } else {
          azi = atan2(-upside_down_factor * cameraViewUp[1], -upside_down_factor * cameraViewUp[0]);
      }
    }
    double D = sqrt(P[0] * P[0] + P[1] * P[1] + P[2] * P[2]);
    double azi_new = azi + rxf / 60.0;
    double elev_new = elev + upside_down_factor * ryf / 60.0;
    double Hnew = D * cos(elev_new);

    double Pnew[3] = {
        Hnew * cos(azi_new),
        Hnew * sin(azi_new),
        D * sin(elev_new)
    };

    double up_z = upside_down_factor * cos(elev_new);
    double up_h = upside_down_factor * sin(elev_new);

    double up_new[3] = {
        -up_h * cos(azi_new),
        -up_h * sin(azi_new),
        up_z
    };

    double new_pos[3] = {
        cameraFocalPoint[0] + Pnew[0],
        cameraFocalPoint[1] + Pnew[1],
        cameraFocalPoint[2] + Pnew[2]
    };

    camera->SetViewUp(up_new);
    camera->SetPosition(new_pos);
    }
  camera->OrthogonalizeViewUp();
  this->CameraModifyEnd(wasCameraNodeModified, true, true);

  this->PreviousEventPosition[0] = eventPosition[0];
  this->PreviousEventPosition[1] = eventPosition[1];

  return true;
}

```

I particularly changed the code after

```auto
  if (this->CameraTiltLocked == true)
    {
    camera->Azimuth(rxf);
    }
  else
    { # changes are made here

```

I hope you will find it usefull as well 🙂

The code was rewritten to C++ based on this py-file:  
[https://github.com/RubendeBruin/DAVE/blob/d0dec40c9a2e9416f31edabfeaf1f769fb6eca51/src/DAVE/visual\_helpers/vtkBlenderLikeInteractionStyle.py#L4](https://github.com/RubendeBruin/DAVE/blob/d0dec40c9a2e9416f31edabfeaf1f769fb6eca51/src/DAVE/visual_helpers/vtkBlenderLikeInteractionStyle.py#L4)

---

<div class="post-metadata">

### Author: ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)
#### Post date: [September 22, 2023, 2:33pm UTC](https://discourse.slicer.org/t/rotation-of-camera-in-3d-view-without-random-tilting/31842/2 "2023-09-22T14:33:13Z")

</div>

Thank you for raising this and providing an example of an additional method for constraining camera rotation in the 3D view.

The camera’s view-up direction may indeed unintentionally spin as the camera is rotated around. Although it is quite easy to fix it by either moving the mouse in a circle or spinning the view using Ctrl + Click-and-drag, it is even better if it can be avoided in the first place. The “Tilt lock” option was added for exactly this reason, but it may be a bit too restrictive to not let users to change the view-up direction at all.

Your code snippet is nice in that it allows tilting but not spinning of the view-up direction, this allows some more freedom than completely locking the view-up direction, but more controlled than letting anything rotated anywhere. However, the current impmlementation you shared has the patient IS axis hardcoded to be the view-up direction. This is great if you want the patient S direction to point upwards, but extremely annoying when you want to make some other axis to point upwards - for example to show a lateral view of the patient (as lying on the CT table). This view orientation is impossible to achieve with code you proposed:

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/0/2/02d6e909b14d467827aeacc4e3f5ae815bf3a128.jpeg)

We cannot remove the current free motion and probably want to keep the “tilt lock” option as well, but we could add a third “Spin lock” option. It would do almost what you implemented, but instead of always keeping the patient IS axis as view-up, it would keep an arbitrarily chosen direction as view-up. The “arbitrarily chosen direction” would actually be the view-up direction at the time when “Spin lock” is activated.

If you can make this improvement we would be happy to add this “Spin lock” option to Slicer core.

---

<div class="post-metadata">

### Author: ![moraleda](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/moraleda/32/77356_2.png) [@moraleda](https://discourse.slicer.org/u/moraleda)
#### Post date: [September 22, 2023, 5:27pm UTC](https://discourse.slicer.org/t/rotation-of-camera-in-3d-view-without-random-tilting/31842/3 "2023-09-22T17:27:36Z")

</div>

Hi @lassoan,

yes, you are right that my code is somewhat restricted. I had not noticed before the issue that you addressed. I will try to add the feature you said.

Thanks!
