Program Listing for File vtkMRMLLayerDMInteractionLogic.cxx

Return to documentation for file (MRMLDM/vtkMRMLLayerDMInteractionLogic.cxx)

#include "vtkMRMLLayerDMInteractionLogic.h"

// Layer DM includes
#include "vtkMRMLLayerDMPipelineI.h"

// Slicer includes
#include "vtkMRMLAbstractWidget.h"
#include "vtkMRMLInteractionEventData.h"

// VTK includes
#include <vtkObjectFactory.h>

vtkStandardNewMacro(vtkMRMLLayerDMInteractionLogic);

vtkMRMLLayerDMPipelineI* vtkMRMLLayerDMInteractionLogic::GetLastFocusedPipeline() const
{
  return this->m_prevFocusedPipeline;
}

vtkMRMLLayerDMInteractionLogic::vtkMRMLLayerDMInteractionLogic()
  : m_prevFocusedPipeline{ nullptr }
  , m_canProcess{}
  , m_viewNode{ nullptr }
{
}

int vtkMRMLLayerDMInteractionLogic::MinWidgetState()
{
  return vtkMRMLAbstractWidget::WidgetStateOnWidget;
}

void vtkMRMLLayerDMInteractionLogic::LoseFocus(vtkMRMLInteractionEventData* eventData)
{
  if (this->m_prevFocusedPipeline)
  {
    this->m_prevFocusedPipeline->LoseFocus(eventData);
    this->m_prevFocusedPipeline = nullptr;
  }
}

void vtkMRMLLayerDMInteractionLogic::LoseFocus()
{
  vtkNew<vtkMRMLInteractionEventData> leaveEvent;
  leaveEvent->SetType(vtkCommand::LeaveEvent);
  leaveEvent->SetViewNode(this->m_viewNode);
  this->LoseFocus(leaveEvent);
}

void vtkMRMLLayerDMInteractionLogic::SetViewNode(vtkMRMLAbstractViewNode* viewNode)
{
  this->m_viewNode = viewNode;
}

std::vector<vtkSmartPointer<vtkMRMLLayerDMPipelineI>> vtkMRMLLayerDMInteractionLogic::GetCanProcessPipelines() const
{
  return this->m_canProcess;
}

std::tuple<double, int> vtkMRMLLayerDMInteractionLogic::PrioritizeCanProcessPipelines(vtkMRMLInteractionEventData* eventData)
{
  // For each pipeline, if pipeline can process, store its state value, layer and distance to interaction
  std::map<vtkMRMLLayerDMPipelineI*, std::tuple<int, unsigned int, double>> priority;
  double minDistance = std::numeric_limits<double>::max();
  int maxState = this->MinWidgetState();
  for (const auto& pipeline : m_pipelines)
  {
    if (pipeline->IsInteractionProcessingBlocked())
    {
      continue;
    }

    double pipelineDistance = std::numeric_limits<double>::max();
    if (pipeline->CanProcessInteractionEvent(eventData, pipelineDistance))
    {
      this->m_canProcess.emplace_back(pipeline);
      int widgetState = std::max(this->MinWidgetState(), pipeline->GetWidgetState());
      minDistance = std::min(minDistance, pipelineDistance);
      maxState = std::max(widgetState, maxState);
      priority[pipeline] = std::make_tuple(widgetState, pipeline->GetRenderOrder(), -pipelineDistance);
    }
  }
  // Sort can process by layer order and inverted square distance (larger layer number first and closest to interaction)
  std::sort(this->m_canProcess.begin(),
            this->m_canProcess.end(),
            [&priority](const vtkSmartPointer<vtkMRMLLayerDMPipelineI>& a, const vtkSmartPointer<vtkMRMLLayerDMPipelineI>& b) { return priority[a] > priority[b]; });

  return std::make_tuple(minDistance, maxState);
}

void vtkMRMLLayerDMInteractionLogic::LosePreviousFocusInCannotProcess(vtkMRMLInteractionEventData* eventData)
{
  // Lose focus if previous focused pipeline cannot process current interaction
  if (std::find(this->m_canProcess.begin(), this->m_canProcess.end(), this->m_prevFocusedPipeline) == this->m_canProcess.end())
  {
    this->LoseFocus(eventData);
  }
}

void vtkMRMLLayerDMInteractionLogic::AddPipeline(const vtkSmartPointer<vtkMRMLLayerDMPipelineI>& pipeline)
{
  if (std::find(this->m_pipelines.begin(), this->m_pipelines.end(), pipeline) != this->m_pipelines.end())
  {
    return;
  }
  this->m_pipelines.emplace_back(pipeline);
}

void vtkMRMLLayerDMInteractionLogic::RemovePipeline(const vtkSmartPointer<vtkMRMLLayerDMPipelineI>& pipeline)
{
  if (this->m_prevFocusedPipeline == pipeline)
  {
    this->LoseFocus();
  }
  this->m_pipelines.erase(std::find(this->m_pipelines.begin(), this->m_pipelines.end(), pipeline));
}

bool vtkMRMLLayerDMInteractionLogic::CanProcessInteractionEvent(vtkMRMLInteractionEventData* eventData, double& distance2)
{
  // Clear previous interaction list
  this->m_canProcess.clear();

  // On leave event lose focus and early return to avoid bad pipeline state
  if (eventData->GetType() == vtkCommand::LeaveEvent)
  {
    this->LoseFocus(eventData);
    return false;
  }

  // Refresh the can process pipelines and order them by priority
  auto [minDistance, maxState] = this->PrioritizeCanProcessPipelines(eventData);

  // Lose previous focus if not in can process list
  this->LosePreviousFocusInCannotProcess(eventData);

  // Return lowest double value if any pipeline can process and is not idle
  // Otherwise, return the min distance returned by the processes.
  distance2 = maxState > this->MinWidgetState() ? std::numeric_limits<double>::lowest() : minDistance;
  return !this->m_canProcess.empty();
}

bool vtkMRMLLayerDMInteractionLogic::ProcessInteractionEvent(vtkMRMLInteractionEventData* eventData)
{
  for (const auto& pipeline : m_canProcess)
  {
    if (pipeline->IsInteractionProcessingBlocked())
    {
      continue;
    }

    // If pipeline can process, store pipeline for further interaction events
    if (pipeline->ProcessInteractionEvent(eventData))
    {
      if (pipeline != this->m_prevFocusedPipeline)
      {
        this->LoseFocus(eventData);
      }
      this->m_prevFocusedPipeline = pipeline;
      return true;
    }
  }

  // If no pipeline was able to process interaction, lose focus
  this->LoseFocus(eventData);
  return false;
}