Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Make default slice view orientations configurable #5536

Merged
merged 1 commit into from Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions Base/QTGUI/Resources/UI/qSlicerSettingsViewsPanel.ui
Expand Up @@ -127,6 +127,20 @@
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="SliceViewOrientationLabel">
<property name="text">
<string>View orientation:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="ctkComboBox" name="SliceViewOrientationComboBox">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
Expand Down
25 changes: 25 additions & 0 deletions Base/QTGUI/qSlicerSettingsViewsPanel.cxx
Expand Up @@ -27,6 +27,7 @@

// CTK includes
#include <ctkVTKAbstractView.h>
#include <ctkComboBox.h>

// --------------------------------------------------------------------------
// qSlicerSettingsViewsPanelPrivate
Expand Down Expand Up @@ -101,6 +102,15 @@ void qSlicerSettingsViewsPanelPrivate::init()
"Slice view ruler type",
ctkSettingsPanel::OptionRequireRestart);

this->SliceViewOrientationComboBox->addItem(QWidget::tr("patient right is screen left (default)"), QString("PatientRightIsScreenLeft"));
this->SliceViewOrientationComboBox->addItem(QWidget::tr("patient right is screen right"), QString("PatientRightIsScreenRight"));
q->registerProperty("DefaultSliceView/Orientation", this->SliceViewOrientationComboBox,
"currentUserDataAsString", SIGNAL(currentIndexChanged(int)),
"Default slice view orientation",
ctkSettingsPanel::OptionRequireRestart);
QObject::connect(this->SliceViewOrientationComboBox, SIGNAL(activated(int)),
q, SLOT(sliceViewOrientationChangedByUser()));

q->registerProperty("Default3DView/BoxVisibility", this->ThreeDBoxVisibilityCheckBox,
"checked", SIGNAL(toggled(bool)),
"3D view cube visibility");
Expand Down Expand Up @@ -275,3 +285,18 @@ void qSlicerSettingsViewsPanel::setThreeDRulerType(const QString& text)
// default to first item if conversion fails
d->ThreeDRulerTypeComboBox->setCurrentIndex(qMax(d->ThreeDRulerTypeComboBox->findText(text), 0));
}

// --------------------------------------------------------------------------
void qSlicerSettingsViewsPanel::sliceViewOrientationChangedByUser()
{
Q_D(qSlicerSettingsViewsPanel);
if (d->SliceViewOrientationComboBox->currentUserDataAsString() == "PatientRightIsScreenRight")
{
if (d->SliceOrientationMarkerTypeComboBox->currentText() == "none")
{
// Non-default orientation is chosen and no orientation marker is displayed.
// To ensure that there is no accidental mixup of orientations, show the orientation marker.
d->SliceOrientationMarkerTypeComboBox->setCurrentText("axes");
}
}
}
1 change: 1 addition & 0 deletions Base/QTGUI/qSlicerSettingsViewsPanel.h
Expand Up @@ -72,6 +72,7 @@ public slots:
void setThreeDOrientationMarkerType(const QString&);
void setThreeDOrientationMarkerSize(const QString&);
void setThreeDRulerType(const QString&);
void sliceViewOrientationChangedByUser();

signals:
/// Signal emitted when the current value is changed
Expand Down
16 changes: 8 additions & 8 deletions Libs/MRML/Core/Testing/vtkMRMLSliceNodeTest1.cxx
Expand Up @@ -57,21 +57,21 @@ void AddSliceOrientationPresets(vtkMRMLSliceNode* sliceNode)
{
{
vtkNew<vtkMatrix3x3> preset;
vtkMRMLSliceNode::InitializeAxialMatrix(preset.GetPointer());
vtkMRMLSliceNode::GetAxialSliceToRASMatrix(preset.GetPointer());

sliceNode->AddSliceOrientationPreset("Axial", preset.GetPointer());
}

{
vtkNew<vtkMatrix3x3> preset;
vtkMRMLSliceNode::InitializeSagittalMatrix(preset.GetPointer());
vtkMRMLSliceNode::GetSagittalSliceToRASMatrix(preset.GetPointer());

sliceNode->AddSliceOrientationPreset("Sagittal", preset.GetPointer());
}

{
vtkNew<vtkMatrix3x3> preset;
vtkMRMLSliceNode::InitializeCoronalMatrix(preset.GetPointer());
vtkMRMLSliceNode::GetCoronalSliceToRASMatrix(preset.GetPointer());

sliceNode->AddSliceOrientationPreset("Coronal", preset.GetPointer());
}
Expand Down Expand Up @@ -295,10 +295,10 @@ int GetSliceOrientationPresetNameTest()
vtkNew<vtkMRMLSliceNode> sliceNode;

vtkNew<vtkMatrix3x3> originalPreset;
vtkMRMLSliceNode::InitializeAxialMatrix(originalPreset.GetPointer());
vtkMRMLSliceNode::GetAxialSliceToRASMatrix(originalPreset.GetPointer());

vtkNew<vtkMatrix3x3> preset;
vtkMRMLSliceNode::InitializeAxialMatrix(preset.GetPointer());
vtkMRMLSliceNode::GetAxialSliceToRASMatrix(preset.GetPointer());
sliceNode->AddSliceOrientationPreset("Axial", preset.GetPointer());


Expand Down Expand Up @@ -413,15 +413,15 @@ int SetOrientationTest()
int InitializeDefaultMatrixTest()
{
vtkNew<vtkMatrix3x3> axial;
vtkMRMLSliceNode::InitializeAxialMatrix(axial.GetPointer());
vtkMRMLSliceNode::GetAxialSliceToRASMatrix(axial.GetPointer());
CHECK_NOT_NULL(axial.GetPointer());

vtkNew<vtkMatrix3x3> coronal;
vtkMRMLSliceNode::InitializeCoronalMatrix(coronal.GetPointer());
vtkMRMLSliceNode::GetCoronalSliceToRASMatrix(coronal.GetPointer());
CHECK_NOT_NULL(coronal.GetPointer());

vtkNew<vtkMatrix3x3> sagittal;
vtkMRMLSliceNode::InitializeSagittalMatrix(sagittal.GetPointer());
vtkMRMLSliceNode::GetSagittalSliceToRASMatrix(sagittal.GetPointer());
CHECK_NOT_NULL(sagittal.GetPointer());

return EXIT_SUCCESS;
Expand Down
117 changes: 86 additions & 31 deletions Libs/MRML/Core/vtkMRMLSliceNode.cxx
Expand Up @@ -433,8 +433,8 @@ bool vtkMRMLSliceNode::AddSliceOrientationPreset(const std::string &name, vtkMat
{
if (it->first == name)
{
vtkDebugMacro("AddSliceOrientationPreset: the orientation preset " << name << " is already stored.");
return false;
it->second->DeepCopy(orientationMatrix);
return true;
}
}

Expand Down Expand Up @@ -517,61 +517,116 @@ bool vtkMRMLSliceNode::HasSliceOrientationPreset(const std::string &name)
}

//----------------------------------------------------------------------------
void vtkMRMLSliceNode::InitializeAxialMatrix(vtkMatrix3x3* orientationMatrix)
void vtkMRMLSliceNode::GetAxialSliceToRASMatrix(vtkMatrix3x3* orientationMatrix, bool patientRightIsScreenLeft/*=true*/)
{
if (!orientationMatrix)
{
return;
}
orientationMatrix->SetElement(0, 0, -1.0);
orientationMatrix->SetElement(1, 0, 0.0);
orientationMatrix->SetElement(2, 0, 0.0);
orientationMatrix->SetElement(0, 1, 0.0);
orientationMatrix->SetElement(1, 1, 1.0);
orientationMatrix->SetElement(2, 1, 0.0);
orientationMatrix->SetElement(0, 2, 0.0);
orientationMatrix->SetElement(1, 2, 0.0);
orientationMatrix->SetElement(2, 2, 1.0);
if (patientRightIsScreenLeft)
{
// L
orientationMatrix->SetElement(0, 0, -1.0);
orientationMatrix->SetElement(1, 0, 0.0);
orientationMatrix->SetElement(2, 0, 0.0);

// A
orientationMatrix->SetElement(0, 1, 0.0);
orientationMatrix->SetElement(1, 1, 1.0);
orientationMatrix->SetElement(2, 1, 0.0);

// I = cross(L, A)
orientationMatrix->SetElement(0, 2, 0.0);
orientationMatrix->SetElement(1, 2, 0.0);
orientationMatrix->SetElement(2, 2, -1.0);
}
else
{
// R
orientationMatrix->SetElement(0, 0, 1.0);
orientationMatrix->SetElement(1, 0, 0.0);
orientationMatrix->SetElement(2, 0, 0.0);

// A
orientationMatrix->SetElement(0, 1, 0.0);
orientationMatrix->SetElement(1, 1, 1.0);
orientationMatrix->SetElement(2, 1, 0.0);

// S = cross(R, A)
orientationMatrix->SetElement(0, 2, 0.0);
orientationMatrix->SetElement(1, 2, 0.0);
orientationMatrix->SetElement(2, 2, 1.0);
}
}

//----------------------------------------------------------------------------
void vtkMRMLSliceNode::InitializeSagittalMatrix(vtkMatrix3x3* orientationMatrix)
void vtkMRMLSliceNode::GetSagittalSliceToRASMatrix(vtkMatrix3x3* orientationMatrix, bool /*patientRightIsScreenLeft=true*/)
{
if (!orientationMatrix)
{
return;
}
// P
orientationMatrix->SetElement(0, 0, 0.0);
orientationMatrix->SetElement(1, 0, -1.0);
orientationMatrix->SetElement(2, 0, 0.0);

// S
orientationMatrix->SetElement(0, 1, 0.0);
orientationMatrix->SetElement(1, 1, 0.0);
orientationMatrix->SetElement(2, 1, 1.0);
orientationMatrix->SetElement(0, 2, 1.0);

// L = cross(P, S)
orientationMatrix->SetElement(0, 2, -1.0);
orientationMatrix->SetElement(1, 2, 0.0);
orientationMatrix->SetElement(2, 2, 0.0);
}

//----------------------------------------------------------------------------
void vtkMRMLSliceNode::InitializeCoronalMatrix(vtkMatrix3x3* orientationMatrix)
void vtkMRMLSliceNode::GetCoronalSliceToRASMatrix(vtkMatrix3x3* orientationMatrix, bool patientRightIsScreenLeft/*=true*/)
{
if (!orientationMatrix)
{
return;
}
orientationMatrix->SetElement(0, 0, -1.0);
orientationMatrix->SetElement(1, 0, 0.0);
orientationMatrix->SetElement(2, 0, 0.0);
orientationMatrix->SetElement(0, 1, 0.0);
orientationMatrix->SetElement(1, 1, 0.0);
orientationMatrix->SetElement(2, 1, 1.0);
orientationMatrix->SetElement(0, 2, 0.0);
orientationMatrix->SetElement(1, 2, 1.0);
orientationMatrix->SetElement(2, 2, 0.0);
if (patientRightIsScreenLeft)
{
// L
orientationMatrix->SetElement(0, 0, -1.0);
orientationMatrix->SetElement(1, 0, 0.0);
orientationMatrix->SetElement(2, 0, 0.0);

// S
orientationMatrix->SetElement(0, 1, 0.0);
orientationMatrix->SetElement(1, 1, 0.0);
orientationMatrix->SetElement(2, 1, 1.0);

// A = cross (L, S)
orientationMatrix->SetElement(0, 2, 0.0);
orientationMatrix->SetElement(1, 2, 1.0);
orientationMatrix->SetElement(2, 2, 0.0);
}
else
{
// R
orientationMatrix->SetElement(0, 0, 1.0);
orientationMatrix->SetElement(1, 0, 0.0);
orientationMatrix->SetElement(2, 0, 0.0);

// S
orientationMatrix->SetElement(0, 1, 0.0);
orientationMatrix->SetElement(1, 1, 0.0);
orientationMatrix->SetElement(2, 1, 1.0);

// P = cross(R, S)
orientationMatrix->SetElement(0, 2, 0.0);
orientationMatrix->SetElement(1, 2, -1.0);
orientationMatrix->SetElement(2, 2, 0.0);
}
}

//----------------------------------------------------------------------------
void vtkMRMLSliceNode::AddDefaultSliceOrientationPresets(vtkMRMLScene* scene)
void vtkMRMLSliceNode::AddDefaultSliceOrientationPresets(vtkMRMLScene* scene, bool patientRightIsScreenLeft/*=true*/)
{
if (!scene)
{
Expand All @@ -580,13 +635,13 @@ void vtkMRMLSliceNode::AddDefaultSliceOrientationPresets(vtkMRMLScene* scene)

// Setting Orientation Matrices presets
vtkNew<vtkMatrix3x3> axialSliceToRAS;
vtkMRMLSliceNode::InitializeAxialMatrix(axialSliceToRAS.GetPointer());
vtkMRMLSliceNode::GetAxialSliceToRASMatrix(axialSliceToRAS, patientRightIsScreenLeft);

vtkNew<vtkMatrix3x3> sagittalSliceToRAS;
vtkMRMLSliceNode::InitializeSagittalMatrix(sagittalSliceToRAS.GetPointer());
vtkMRMLSliceNode::GetSagittalSliceToRASMatrix(sagittalSliceToRAS, patientRightIsScreenLeft);

vtkNew<vtkMatrix3x3> coronalSliceToRAS;
vtkMRMLSliceNode::InitializeCoronalMatrix(coronalSliceToRAS.GetPointer());
vtkMRMLSliceNode::GetCoronalSliceToRASMatrix(coronalSliceToRAS, patientRightIsScreenLeft);

// Setting a Slice Default Node
vtkSmartPointer<vtkMRMLNode> defaultNode = scene->GetDefaultNodeByClass("vtkMRMLSliceNode");
Expand All @@ -596,9 +651,9 @@ void vtkMRMLSliceNode::AddDefaultSliceOrientationPresets(vtkMRMLScene* scene)
scene->AddDefaultNode(defaultNode);
}
vtkMRMLSliceNode * defaultSliceNode = vtkMRMLSliceNode::SafeDownCast(defaultNode);
defaultSliceNode->AddSliceOrientationPreset("Axial", axialSliceToRAS.GetPointer());
defaultSliceNode->AddSliceOrientationPreset("Sagittal", sagittalSliceToRAS.GetPointer());
defaultSliceNode->AddSliceOrientationPreset("Coronal", coronalSliceToRAS.GetPointer());
defaultSliceNode->AddSliceOrientationPreset("Axial", axialSliceToRAS);
defaultSliceNode->AddSliceOrientationPreset("Sagittal", sagittalSliceToRAS);
defaultSliceNode->AddSliceOrientationPreset("Coronal", coronalSliceToRAS);
}

//----------------------------------------------------------------------------
Expand Down
23 changes: 14 additions & 9 deletions Libs/MRML/Core/vtkMRMLSliceNode.h
Expand Up @@ -205,7 +205,7 @@ class VTK_MRML_EXPORT vtkMRMLSliceNode : public vtkMRMLAbstractViewNode
/// \sa AddSliceOrientationPreset(const std::string& name, vtkMatrix3x3* orientationMatrix)
int GetNumberOfSliceOrientationPresets() const;

/// \brief Add an orientation preset.
/// \brief Add or update an orientation preset.
///
/// \sa RenameSliceOrientationPreset(const std::string& name, const std::string& updatedName)
/// \sa RemoveSliceOrientationPreset(const std::string& name)
Expand All @@ -231,21 +231,26 @@ class VTK_MRML_EXPORT vtkMRMLSliceNode : public vtkMRMLAbstractViewNode
static const char* GetReformatOrientationName() { return "Reformat"; }

/// \brief Initialize \a orientationMatrix as an `Axial` orientation matrix.
static void InitializeAxialMatrix(vtkMatrix3x3* orientationMatrix);
/// \param patientRightIsScreenLeft chooses between radiology (default, patient right is left side on the screen)
/// and neurology (patient right is right side on the screen) view orientation conventions.
static void GetAxialSliceToRASMatrix(vtkMatrix3x3* orientationMatrix, bool patientRightIsScreenLeft=true);

/// \brief Initialize \a orientationMatrix as a `Sagittal` orientation matrix.
static void InitializeSagittalMatrix(vtkMatrix3x3* orientationMatrix);
/// \param patientRightIsScreenLeft chooses between radiology (default, patient right is left side on the screen)
/// and neurology (patient right is right side on the screen) view orientation conventions.
static void GetSagittalSliceToRASMatrix(vtkMatrix3x3* orientationMatrix, bool patientRightIsScreenLeft=true);

/// \brief Initialize \a orientationMatrix as a `Coronal` orientation matrix.
static void InitializeCoronalMatrix(vtkMatrix3x3* orientationMatrix);
/// \param patientRightIsScreenLeft chooses between radiology (default, patient right is left side on the screen)
/// and neurology (patient right is right side on the screen) view orientation conventions.
static void GetCoronalSliceToRASMatrix(vtkMatrix3x3* orientationMatrix, bool patientRightIsScreenLeft=true);

/// \brief Add default slice orientation presets to \a scene.
///
/// \param patientRightIsScreenLeft chooses between radiology (default, patient right is left side on the screen)
/// and neurology (patient right is right side on the screen) view orientation conventions.
/// \sa vtkMRMLScene::AddDefaultNode(vtkMRMLNode* node)
/// \sa InitializeAxialMatrix(vtkMatrix3x3* orientationMatrix)
/// \sa InitializeSagittalMatrix(vtkMatrix3x3* orientationMatrix)
/// \sa InitializeCoronalMatrix(vtkMatrix3x3* orientationMatrix)
static void AddDefaultSliceOrientationPresets(vtkMRMLScene* scene);
/// \sa GetAxialSliceToRASMatrix, GetSagittalSliceToRASMatrix, GetCoronalSliceToRASMatrix
static void AddDefaultSliceOrientationPresets(vtkMRMLScene* scene, bool patientRightIsScreenLeft=true);

///
/// Size of the slice plane in millimeters
Expand Down
Expand Up @@ -87,7 +87,7 @@ void qMRMLSliceControllerWidgetTester::init()
vtkNew<vtkMRMLSliceNode> sliceNode;
sliceNode->SetLayoutName("Red");
vtkNew<vtkMatrix3x3> axialSliceToRAS;
vtkMRMLSliceNode::InitializeAxialMatrix(axialSliceToRAS.GetPointer());
vtkMRMLSliceNode::GetAxialSliceToRASMatrix(axialSliceToRAS.GetPointer());

sliceNode->AddSliceOrientationPreset("Axial", axialSliceToRAS.GetPointer());
sliceNode->SetOrientation("Axial");
Expand Down
Expand Up @@ -133,7 +133,13 @@ void vtkSlicerViewControllersLogic::ResetAllViewNodesToDefault()
scene->GetNodesByClass("vtkMRMLSliceNode", viewNodes);
for (std::vector< vtkMRMLNode* >::iterator it = viewNodes.begin(); it != viewNodes.end(); ++it)
{
(*it)->Reset(defaultSliceViewNode);
vtkMRMLSliceNode* sliceNode = vtkMRMLSliceNode::SafeDownCast(*it);
if (!sliceNode)
{
continue;
}
sliceNode->Reset(defaultSliceViewNode);
sliceNode->SetOrientationToDefault();
}
viewNodes.clear();
vtkMRMLViewNode* defaultThreeDViewNode = GetDefaultThreeDViewNode();
Expand Down