From d4e34a1f0a1c3ef1e977e0798c29a5c577a1ebdd Mon Sep 17 00:00:00 2001 From: Steve Pieper Date: Tue, 15 Jan 2019 17:24:20 -0500 Subject: [PATCH 1/3] ENH: constrain fiducial movement to visible surfaces This adds a picker step when updating MRML markup fiducial location from the widget, which sets the location to stay on the visible surface (like it does when first placed). The location of the fiducial gets out of sync with the mouse location as you drag, but the interaction still seems intuitive and useful compared with the previous behavior. --- .../vtkThreeDViewInteractorStyle.h | 8 ++-- ...RMLMarkupsFiducialDisplayableManager3D.cxx | 48 ++++++++++++++++++- ...kMRMLMarkupsFiducialDisplayableManager3D.h | 8 ++++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Libs/MRML/DisplayableManager/vtkThreeDViewInteractorStyle.h b/Libs/MRML/DisplayableManager/vtkThreeDViewInteractorStyle.h index 8d9fa72eb..2f93c09f0 100644 --- a/Libs/MRML/DisplayableManager/vtkThreeDViewInteractorStyle.h +++ b/Libs/MRML/DisplayableManager/vtkThreeDViewInteractorStyle.h @@ -22,13 +22,13 @@ // VTK includes #include "vtkObject.h" +#include "vtkCellPicker.h" #include "vtkInteractorStyle.h" #include "vtkSmartPointer.h" #include "vtkMRMLDisplayableManagerExport.h" class vtkMRMLModelDisplayableManager; -class vtkCellPicker; /// \brief Interactive manipulation of the camera. /// @@ -102,12 +102,14 @@ public: vtkGetObjectMacro(ModelDisplayableManager, vtkMRMLModelDisplayableManager); virtual void SetModelDisplayableManager(vtkMRMLModelDisplayableManager *modelDisplayableManager); + /// Use the internal picker to find closest intersected point + /// along ray from camera through x, y in display coordinates + bool Pick(int x, int y, double pickPoint[3]); + protected: vtkThreeDViewInteractorStyle(); ~vtkThreeDViewInteractorStyle(); - bool Pick(int x, int y, double pickPoint[3]); - vtkMRMLCameraNode *CameraNode; double MotionFactor; diff --git a/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.cxx b/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.cxx index 2f0007b53..f3711ed6a 100644 --- a/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.cxx +++ b/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.cxx @@ -27,7 +27,7 @@ #include // MRMLDisplayableManager includes -#include +#include // MRML includes #include @@ -37,6 +37,7 @@ // VTK includes #include +#include #include #include #include @@ -616,6 +617,46 @@ void vtkMRMLMarkupsFiducialDisplayableManager3D::PropagateMRMLToWidget(vtkMRMLMa this->Updating = 0; } +//--------------------------------------------------------------------------- +/// Move on surface of what's visible in the current ThreeDView +/// Set coord to new pick position +void vtkMRMLMarkupsFiducialDisplayableManager3D::ConstrainMovement(vtkMRMLMarkupsFiducialNode *fiducialNode, double coord[3]) +{ + vtkAbstractWidget *widget = this->Helper->GetWidget(fiducialNode); + if (!widget) + { + vtkErrorMacro("ConstrainMovement: no widget for fiducial node."); + return; + } + + vtkSeedWidget* seedWidget = vtkSeedWidget::SafeDownCast(widget); + if (!seedWidget) + { + vtkErrorMacro("ConstrainMovement: Could not get seed widget!") + return; + } + + vtkWidgetRepresentation *widgetRepresentation = seedWidget->GetRepresentation(); + widgetRepresentation->SetPickable(0); + + double displayCoordinates[3]; + this->GetWorldToDisplayCoordinates(coord, displayCoordinates); + + vtkThreeDViewInteractorStyle* interactorStyle = vtkThreeDViewInteractorStyle::SafeDownCast( + this->GetInteractor()->GetInteractorStyle()); + vtkCellPicker *cellPicker = interactorStyle->GetCellPicker(); + + double pickPoint[3]; + if (interactorStyle->Pick(displayCoordinates[0], displayCoordinates[1], pickPoint)) + { + coord[0] = pickPoint[0]; + coord[1] = pickPoint[1]; + coord[2] = pickPoint[2]; + } + + widgetRepresentation->SetPickable(1); +} + //--------------------------------------------------------------------------- /// Propagate properties of widget to MRML node. void vtkMRMLMarkupsFiducialDisplayableManager3D::PropagateWidgetToMRML(vtkAbstractWidget * widget, vtkMRMLMarkupsNode* node) @@ -680,6 +721,10 @@ void vtkMRMLMarkupsFiducialDisplayableManager3D::PropagateWidgetToMRML(vtkAbstra << ", " << currentCoordinates[1] << ", " << currentCoordinates[2]); + // e.g. stick to model surface of associated node + this->ConstrainMovement(fiducialNode, worldCoordinates1); + + // make three element arrays for comparison double currentCoords[3]; currentCoords[0] = currentCoordinates[0]; currentCoords[1] = currentCoordinates[1]; @@ -688,6 +733,7 @@ void vtkMRMLMarkupsFiducialDisplayableManager3D::PropagateWidgetToMRML(vtkAbstra newCoords[0] = worldCoordinates1[0]; newCoords[1] = worldCoordinates1[1]; newCoords[2] = worldCoordinates1[2]; + if (this->GetWorldCoordinatesChanged(currentCoords, newCoords)) { positionChanged = true; diff --git a/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.h b/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.h index e23db4077..54455015d 100644 --- a/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.h +++ b/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.h @@ -66,6 +66,14 @@ protected: /// Propagate properties of widget to MRML node. virtual void PropagateWidgetToMRML(vtkAbstractWidget * widget, vtkMRMLMarkupsNode* node) VTK_OVERRIDE; + /// Constrain movement along surface of displayables in the scene + /// NOTE: this method is added to the Markups infrastructure to address a common use + /// case of dragging a fiducial on a 3D surface. Since their is an active development + /// process in place to create a new design, this feature is not added as a general + /// purpose virtual method. + /// See: https://na-mic.github.io/ProjectWeek/PW30_2019_GranCanaria/Projects/MarkupsRedesign/ + void ConstrainMovement(vtkMRMLMarkupsFiducialNode *fiducialNode, double coords[3]); + /// Set up an observer on the interactor style to watch for key press events virtual void AdditionnalInitializeStep(); /// Respond to the interactor style event -- GitLab From 2debc3505f722dc0d4d9a3afcccbb7f8aa2bf7eb Mon Sep 17 00:00:00 2001 From: Steve Pieper Date: Tue, 15 Jan 2019 17:28:35 -0500 Subject: [PATCH 2/3] ENH: WIP: provide user option to constrain fiducial movement This adds a checkbox to the preference pane, and serializes to the settings file. But it's not clear how to pass this information down to the mrml displayable manager, which only knows VTK and not Qt. It's also not clear how the Markups module widget could communicate with the displayable managers except through mrml, and this setting is meant to be an active setting, and not a property stored in the scene. One option could be to make this a non-serialized property of the MRML interaction node that is always set to the user preference when the application starts. More review is needed before this is merged. --- .../Resources/UI/qSlicerSettingsViewsPanel.ui | 28 +++++++++++++++++++ Base/QTGUI/qSlicerSettingsViewsPanel.cxx | 5 ++++ 2 files changed, 33 insertions(+) diff --git a/Base/QTGUI/Resources/UI/qSlicerSettingsViewsPanel.ui b/Base/QTGUI/Resources/UI/qSlicerSettingsViewsPanel.ui index 7d9cde6f4..9c485d2af 100644 --- a/Base/QTGUI/Resources/UI/qSlicerSettingsViewsPanel.ui +++ b/Base/QTGUI/Resources/UI/qSlicerSettingsViewsPanel.ui @@ -311,6 +311,34 @@ p, li { white-space: pre-wrap; } + + + + + + Constrain fiducials to 3D surface: + + + + + + + + 0 + 0 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Keep fiducials on the surfaces of visible objects in the 3D view.</span></p></body></html> + + + + + diff --git a/Base/QTGUI/qSlicerSettingsViewsPanel.cxx b/Base/QTGUI/qSlicerSettingsViewsPanel.cxx index 480f5511e..df7d72c46 100644 --- a/Base/QTGUI/qSlicerSettingsViewsPanel.cxx +++ b/Base/QTGUI/qSlicerSettingsViewsPanel.cxx @@ -132,6 +132,11 @@ void qSlicerSettingsViewsPanelPrivate::init() "checked", SIGNAL(toggled(bool)), "Orthographic projection"); + q->registerProperty("ThreeDView/ConstrainFiducials", this->ThreeDConstrainMarkupsCheckBox, + "checked", SIGNAL(toggled(bool)), + "Orthographic projection", + ctkSettingsPanel::OptionRequireRestart); + } // -------------------------------------------------------------------------- -- GitLab From 39c633369a8a76868190e9cff1e2b40c21bc32c4 Mon Sep 17 00:00:00 2001 From: Steve Pieper Date: Sat, 19 Jan 2019 14:48:41 -0500 Subject: [PATCH 3/3] STYLE: remove unused variable --- .../MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.cxx b/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.cxx index f3711ed6a..a3354dc10 100644 --- a/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.cxx +++ b/Modules/Loadable/Markups/MRMLDM/vtkMRMLMarkupsFiducialDisplayableManager3D.cxx @@ -644,7 +644,6 @@ void vtkMRMLMarkupsFiducialDisplayableManager3D::ConstrainMovement(vtkMRMLMarkup vtkThreeDViewInteractorStyle* interactorStyle = vtkThreeDViewInteractorStyle::SafeDownCast( this->GetInteractor()->GetInteractorStyle()); - vtkCellPicker *cellPicker = interactorStyle->GetCellPicker(); double pickPoint[3]; if (interactorStyle->Pick(displayCoordinates[0], displayCoordinates[1], pickPoint)) -- GitLab