Program Listing for File vtkMRMLLayerDMCameraSynchronizer.cxx
↰ Return to documentation for file (MRMLDM/vtkMRMLLayerDMCameraSynchronizer.cxx)
#include "vtkMRMLLayerDMCameraSynchronizer.h"
// Layer DM includes
#include "vtkMRMLLayerDMObjectEventObserver.h"
// Slicer includes
#include "vtkMRMLAbstractViewNode.h"
#include "vtkMRMLSliceNode.h"
// VTK includes
#include <vtkCamera.h>
#include <vtkMatrix4x4.h>
#include <vtkObjectFactory.h>
#include <vtkRenderer.h>
// STL includes
#include <array>
class CameraSynchronizeStrategy
{
public:
explicit CameraSynchronizeStrategy(const vtkSmartPointer<vtkCamera>& camera, std::function<void()> invokeModifiedEvent)
: m_camera(camera)
, m_invokeModifiedEvent{ std::move(invokeModifiedEvent) }
{
}
virtual ~CameraSynchronizeStrategy() = default;
virtual void UpdateCamera() = 0;
protected:
vtkSmartPointer<vtkCamera> m_camera;
vtkNew<vtkMRMLLayerDMObjectEventObserver> m_eventObserver;
std::function<void()> m_invokeModifiedEvent;
};
class DefaultCameraSynchronizeStrategy : public CameraSynchronizeStrategy
{
public:
explicit DefaultCameraSynchronizeStrategy(const vtkSmartPointer<vtkCamera>& camera, vtkRenderer* renderer, std::function<void()> invokeModifiedEvent)
: CameraSynchronizeStrategy(camera, std::move(invokeModifiedEvent))
, m_renderer(renderer)
{
this->m_eventObserver->SetUpdateCallback(
[this](vtkObject* object)
{
if (object == this->m_renderer)
{
this->ObserveActiveCamera();
}
this->UpdateCamera();
});
this->m_eventObserver->UpdateObserver(nullptr, this->m_renderer, vtkCommand::ActiveCameraEvent);
this->ObserveActiveCamera();
}
void UpdateCamera() override
{
if (!this->m_observedCamera)
{
return;
}
// Update camera and preserve clipping range
double clippingRange[2];
this->m_camera->GetClippingRange(clippingRange);
this->m_camera->DeepCopy(this->m_observedCamera);
this->m_camera->SetClippingRange(clippingRange);
this->m_invokeModifiedEvent();
}
private:
void ObserveActiveCamera() { this->SetObservedCamera(this->m_renderer ? this->m_renderer->GetActiveCamera() : nullptr); }
void SetObservedCamera(vtkCamera* camera)
{
if (this->m_observedCamera == camera)
{
return;
}
this->m_eventObserver->UpdateObserver(this->m_observedCamera, camera);
this->m_observedCamera = camera;
}
vtkWeakPointer<vtkRenderer> m_renderer;
vtkWeakPointer<vtkCamera> m_observedCamera;
};
class SliceViewCameraSynchronizeStrategy : public CameraSynchronizeStrategy
{
public:
explicit SliceViewCameraSynchronizeStrategy(const vtkSmartPointer<vtkCamera>& camera, vtkMRMLSliceNode* sliceNode, std::function<void()> invokeModifiedEvent)
: CameraSynchronizeStrategy(camera, std::move(invokeModifiedEvent))
, m_sliceNode{ sliceNode }
{
this->m_eventObserver->SetUpdateCallback(
[this](vtkObject* object)
{
if (object == this->m_sliceNode)
{
this->UpdateCamera();
}
});
this->m_eventObserver->UpdateObserver(nullptr, this->m_sliceNode);
}
void UpdateCamera() override
{
if (!this->m_sliceNode)
{
return;
}
// Compute view center
vtkMatrix4x4* xyToRas = this->m_sliceNode->GetXYToRAS();
std::array<double, 4> viewCenterXY = { 0.5 * this->m_sliceNode->GetDimensions()[0], 0.5 * this->m_sliceNode->GetDimensions()[1], 0.0, 1.0 };
std::array<double, 4> viewCenterRAS = {};
xyToRas->MultiplyPoint(viewCenterXY.data(), viewCenterRAS.data());
// Current slice RAS coordinate is invalid (Slice was probably just created and not already displayed).
// Avoid propagating NaN.
if (std::isnan(viewCenterRAS[0]))
{
return;
}
// Parallel projection and scale
this->m_camera->ParallelProjectionOn();
this->m_camera->SetParallelScale(0.5 * this->m_sliceNode->GetFieldOfView()[1]);
// Set focal point
this->m_camera->SetFocalPoint(viewCenterRAS.data());
// View directions
vtkMatrix4x4* sliceToRAS = this->m_sliceNode->GetSliceToRAS();
std::array<double, 3> vRight = { sliceToRAS->GetElement(0, 0), sliceToRAS->GetElement(1, 0), sliceToRAS->GetElement(2, 0) };
std::array<double, 3> vUp = { sliceToRAS->GetElement(0, 1), sliceToRAS->GetElement(1, 1), sliceToRAS->GetElement(2, 1) };
this->m_camera->SetViewUp(vUp.data());
// Position
double d = this->m_camera->GetDistance();
std::array<double, 3> normal{};
vtkMath::Cross(vRight.data(), vUp.data(), normal.data());
double position[3] = { viewCenterRAS[0] + normal[0] * d, viewCenterRAS[1] + normal[1] * d, viewCenterRAS[2] + normal[2] * d };
this->m_camera->SetPosition(position);
this->m_invokeModifiedEvent();
}
private:
vtkWeakPointer<vtkMRMLSliceNode> m_sliceNode;
};
vtkStandardNewMacro(vtkMRMLLayerDMCameraSynchronizer);
void vtkMRMLLayerDMCameraSynchronizer::SetViewNode(vtkMRMLAbstractViewNode* viewNode)
{
if (this->m_viewNode == viewNode)
{
return;
}
this->m_viewNode = viewNode;
this->UpdateStrategy();
}
void vtkMRMLLayerDMCameraSynchronizer::SetDefaultCamera(const vtkSmartPointer<vtkCamera>& camera)
{
if (this->m_defaultCamera == camera)
{
return;
}
this->m_defaultCamera = camera;
this->UpdateStrategy();
}
void vtkMRMLLayerDMCameraSynchronizer::SetRenderer(vtkRenderer* renderer)
{
if (this->m_renderer == renderer)
{
return;
}
this->m_renderer = renderer;
this->UpdateStrategy();
}
vtkMRMLLayerDMCameraSynchronizer::vtkMRMLLayerDMCameraSynchronizer()
: m_defaultCamera{ nullptr }
, m_renderer{ nullptr }
, m_viewNode{ nullptr }
, m_syncStrategy{ nullptr }
{
}
vtkMRMLLayerDMCameraSynchronizer::~vtkMRMLLayerDMCameraSynchronizer() = default;
void vtkMRMLLayerDMCameraSynchronizer::UpdateStrategy()
{
if (!this->m_defaultCamera || !this->m_renderer)
{
this->m_syncStrategy = nullptr;
return;
}
const auto invokeModifiedEvent = [this]
{
if (this->m_isBlocked)
{
return;
}
this->Modified();
};
if (auto sliceNode = vtkMRMLSliceNode::SafeDownCast(this->m_viewNode))
{
this->m_syncStrategy = std::make_unique<SliceViewCameraSynchronizeStrategy>(this->m_defaultCamera, sliceNode, invokeModifiedEvent);
}
else
{
this->m_syncStrategy = std::make_unique<DefaultCameraSynchronizeStrategy>(this->m_defaultCamera, this->m_renderer, invokeModifiedEvent);
}
this->m_syncStrategy->UpdateCamera();
}
bool vtkMRMLLayerDMCameraSynchronizer::BlockModified(bool isBlocked)
{
bool wasBlocked = this->m_isBlocked;
m_isBlocked = isBlocked;
return wasBlocked;
}