Skip to content

Commit

Permalink
ENH: Add API to change screen scale factor
Browse files Browse the repository at this point in the history
Size of markup control points glyphs and text can be adjusted globally for a view by modifying the view node's ScreenScaleFactor property.
This is useful for compensating different physical screen sizes and resolutions or for taking high-resolution screenshots.
This scaling factor has already existed but it was hardcoded and now it is exposed in the view node.

Example use:

    getNode('vtkMRMLViewNode1').SetScreenScaleFactor(0.1)
    getNode('vtkMRMLSliceNodeRed').SetScreenScaleFactor(0.1)

Most likely users would not want to save this factor in the scene but they would set it for their computer, therefore the value is not written into the scene file.
  • Loading branch information
lassoan committed May 10, 2024
1 parent 1f1dd73 commit 500ea52
Show file tree
Hide file tree
Showing 17 changed files with 73 additions and 40 deletions.
12 changes: 12 additions & 0 deletions Libs/MRML/Core/vtkMRMLAbstractViewNode.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ void vtkMRMLAbstractViewNode::WriteXML(ostream& of, int nIndent)
vtkMRMLWriteXMLEnumMacro(rulerType, RulerType);
}
vtkMRMLWriteXMLEnumMacro(rulerColor, RulerColor);

// Do not write screenScaleFactor attribute, as we should not use the
// value that is in the scene but what the user has set in application settings.
// vtkMRMLWriteXMLFloatMacro(screenScaleFactor, ScreenScaleFactor);

vtkMRMLWriteXMLEndMacro();

of << " AxisLabels=\"";
Expand Down Expand Up @@ -142,6 +147,11 @@ void vtkMRMLAbstractViewNode::ReadXMLAttributes(const char** atts)
vtkMRMLReadXMLEnumMacro(orientationMarkerSize, OrientationMarkerSize);
vtkMRMLReadXMLEnumMacro(rulerType, RulerType);
vtkMRMLReadXMLEnumMacro(rulerColor, RulerColor);

// Do not read screenScaleFactor attribute, as we should not use the
// value that is in the scene but what the user has set in application settings.
// vtkMRMLReadXMLFloatMacro(screenScaleFactor, ScreenScaleFactor);

vtkMRMLReadXMLEndMacro();

const char* attName;
Expand Down Expand Up @@ -227,6 +237,7 @@ void vtkMRMLAbstractViewNode::CopyContent(vtkMRMLNode* anode, bool deepCopy/*=tr
vtkMRMLCopyEnumMacro(RulerType);
}
vtkMRMLCopyEnumMacro(RulerColor);
vtkMRMLCopyFloatMacro(ScreenScaleFactor);
vtkMRMLCopyEndMacro();

vtkMRMLAbstractViewNode *node = vtkMRMLAbstractViewNode::SafeDownCast(anode);
Expand Down Expand Up @@ -275,6 +286,7 @@ void vtkMRMLAbstractViewNode::PrintSelf(ostream& os, vtkIndent indent)
vtkMRMLPrintEnumMacro(RulerType);
}
vtkMRMLPrintEnumMacro(RulerColor);
vtkMRMLPrintFloatMacro(ScreenScaleFactor);
vtkMRMLPrintEndMacro();

os << indent << " AxisLabels: ";
Expand Down
14 changes: 14 additions & 0 deletions Libs/MRML/Core/vtkMRMLAbstractViewNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,16 @@ class VTK_MRML_EXPORT vtkMRMLAbstractViewNode
/// \returns layout node that this view belongs to.
vtkMRMLLayoutNode* GetMaximizedState(bool& maximized, bool& canBeMaximized);

//@{
/// Get/set scaling factor of text and interactive elements in the viewer.
/// If the screen is physically larger then the user prefer a smaller screen scale factor.
/// When using a smaller screen or if the screen is farther or eyesight of the user is not perfect
/// then a larger screen scale factor may be preferable.
/// Setting of this value is still experimental and therefore the current value is not saved into the scene.
vtkGetMacro(ScreenScaleFactor, double);
vtkSetMacro(ScreenScaleFactor, double);
//@}

protected:
vtkMRMLAbstractViewNode();
~vtkMRMLAbstractViewNode() override;
Expand Down Expand Up @@ -340,6 +350,10 @@ class VTK_MRML_EXPORT vtkMRMLAbstractViewNode
int RulerType{RulerTypeNone};
int RulerColor{RulerColorWhite};

/// Default glyph scale used to be 3.0 (in Slicer-4.10 and earlier).
/// This display scale factor value produces similar appearance of markup points.
double ScreenScaleFactor{ 0.2 };

///
/// Labels of coordinate system axes
vtkSmartPointer<vtkStringArray> AxisLabels;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@
//----------------------------------------------------------------------
vtkMRMLAbstractWidgetRepresentation::vtkMRMLAbstractWidgetRepresentation()
{
// Default glyph scale used to be 3.0 (in Slicer-4.10 and earlier).
// This display scale factor value produces similar appearance of markup points.
this->ScreenScaleFactor = 0.2;

this->PickingTolerance = 30.0;
this->NeedToRender = false;

Expand Down Expand Up @@ -115,11 +111,10 @@ void vtkMRMLAbstractWidgetRepresentation::PrintSelf(ostream& os,
//Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h
this->Superclass::PrintSelf(os, indent);
os << indent << "PickingTolerance : " << this->PickingTolerance <<"\n";
os << indent << "ScreenScaleFactor: " << this->ScreenScaleFactor << "\n";
os << indent << "ScreenScaleFactor: " << this->GetScreenScaleFactor() << "\n";
os << indent << "Always On Top: " << (this->AlwaysOnTop ? "On\n" : "Off\n");
}


//-----------------------------------------------------------------------------
void vtkMRMLAbstractWidgetRepresentation::AddActorsBounds(vtkBoundingBox& boundingBox,
const std::vector<vtkProp*> &actors, double* additionalBounds /*=nullptr*/)
Expand Down Expand Up @@ -167,3 +162,17 @@ void vtkMRMLAbstractWidgetRepresentation::UpdateRelativeCoincidentTopologyOffset
mapper->SetRelativeCoincidentTopologyPointOffsetParameter(-1);
}
}

//-----------------------------------------------------------------------------
double vtkMRMLAbstractWidgetRepresentation::GetScreenScaleFactor()
{
if (this->GetViewNode())
{
return this->GetViewNode()->GetScreenScaleFactor();
}
else
{
// Default screen scale factor in view node
return 0.2;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ class VTK_MRML_DISPLAYABLEMANAGER_EXPORT vtkMRMLAbstractWidgetRepresentation : p
vtkBooleanMacro(NeedToRender, bool);
//@}

/// Convenience method for getting screen scale factor from the associated view node.
double GetScreenScaleFactor();

protected:
vtkMRMLAbstractWidgetRepresentation();
~vtkMRMLAbstractWidgetRepresentation() override;
Expand All @@ -168,9 +171,6 @@ class VTK_MRML_DISPLAYABLEMANAGER_EXPORT vtkMRMLAbstractWidgetRepresentation : p
/// For VR renderer it is defined in millimeters. The specified value is scaled with WorldToPhysicalScale.
double PickingTolerance;

/// Allows global rescaling of all widgets (to compensate for larger or smaller physical screen size)
double ScreenScaleFactor;

vtkWeakPointer<vtkMRMLAbstractViewNode> ViewNode;

bool AlwaysOnTop;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ void vtkMRMLInteractionWidgetRepresentation::PrintSelf(ostream& os,
//----------------------------------------------------------------------
double vtkMRMLInteractionWidgetRepresentation::GetMaximumHandlePickingDistance2()
{
double maximumHandlePickingDistance = this->InteractionSize / 10.0 + this->PickingTolerance * this->ScreenScaleFactor;
double maximumHandlePickingDistance = this->InteractionSize / 10.0 + this->PickingTolerance * this->GetScreenScaleFactor();
return maximumHandlePickingDistance * maximumHandlePickingDistance;
}

Expand Down Expand Up @@ -1995,7 +1995,7 @@ void vtkMRMLInteractionWidgetRepresentation::UpdateHandleSize()
{
if (!this->GetInteractionSizeAbsolute())
{
this->InteractionSize = this->ScreenSizePixel * this->ScreenScaleFactor
this->InteractionSize = this->ScreenSizePixel * this->GetScreenScaleFactor()
* this->GetInteractionScalePercent() / 100.0 * this->ViewScaleFactorMmPerPixel;
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2585,7 +2585,7 @@ void vtkMRMLSliceIntersectionInteractionRepresentation::TransformIntersectingSli
//----------------------------------------------------------------------
double vtkMRMLSliceIntersectionInteractionRepresentation::GetMaximumHandlePickingDistance2()
{
double maximumHandlePickingDistance = this->InteractionSize / 2.0 + this->PickingTolerance * this->ScreenScaleFactor;
double maximumHandlePickingDistance = this->InteractionSize / 2.0 + this->PickingTolerance * this->GetScreenScaleFactor();
return maximumHandlePickingDistance * maximumHandlePickingDistance;
}

Expand Down Expand Up @@ -2723,7 +2723,7 @@ std::string vtkMRMLSliceIntersectionInteractionRepresentation::CanInteract(vtkMR
if (interactionEventData->IsDisplayPositionValid())
{
double pixelTolerance = this->InteractionSize / 2.0 / this->GetViewScaleFactorAtPosition(handleWorldPos)
+ this->PickingTolerance * this->ScreenScaleFactor;
+ this->PickingTolerance * this->GetScreenScaleFactor();
this->Renderer->SetWorldPoint(handleWorldPos);
this->Renderer->WorldToDisplay();
this->Renderer->GetDisplayPoint(handleDisplayPos);
Expand Down
6 changes: 2 additions & 4 deletions Modules/Loadable/Markups/MRML/vtkMRMLMarkupsDisplayNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,8 @@ class VTK_SLICER_MARKUPS_MODULE_MRML_EXPORT vtkMRMLMarkupsDisplayNode : public
/// Get/Set markup point size relative to the window size.
/// This value is only used in slice views and only if SliceUseGlyphScale is set to true.
/// Diameter of the point is defined as percentage of "window size".
/// "Window size" is computed as diagonal size of the screen multiplied by ScreenScaleFactor.
/// Currently ScreenScaleFactor is set to to a fixed value of 0.2 (therefore glyph scale = 1.00
/// corresponds to 20% of the screen diagonal size), but this factor may be made configurable
/// in the future.
/// "Window size" is computed as diagonal size of the screen multiplied by ScreenScaleFactor
/// (stored in the view node).
vtkSetMacro(GlyphScale,double);
vtkGetMacro(GlyphScale,double);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ void vtkMRMLMarkupsDisplayableManager::OnMRMLDisplayableNodeModifiedEvent(vtkObj
this->SliceNode = sliceNode;

// now we call the handle for specific sliceNode actions
this->OnMRMLSliceNodeModifiedEvent();
this->OnMRMLViewNodeModifiedEvent(this->SliceNode);

// and exit
return;
Expand All @@ -468,14 +468,14 @@ void vtkMRMLMarkupsDisplayableManager::OnMRMLDisplayableNodeModifiedEvent(vtkObj
vtkMRMLViewNode * viewNode = vtkMRMLViewNode::SafeDownCast(caller);
if (viewNode)
{
// the associated renderWindow is a 3D View
vtkDebugMacro("OnMRMLDisplayableNodeModifiedEvent: This displayableManager handles a ThreeD view.");
// Update all widgets (for example, to respond to ScreenScaleFactor changes)
this->OnMRMLViewNodeModifiedEvent(viewNode);
return;
}
}

//---------------------------------------------------------------------------
void vtkMRMLMarkupsDisplayableManager::OnMRMLSliceNodeModifiedEvent()
void vtkMRMLMarkupsDisplayableManager::OnMRMLViewNodeModifiedEvent(vtkMRMLAbstractViewNode* viewNode)
{
bool renderRequested = false;

Expand All @@ -486,7 +486,7 @@ void vtkMRMLMarkupsDisplayableManager::OnMRMLSliceNodeModifiedEvent()
{
// we loop through all widgets
vtkSlicerMarkupsWidget* widget = (it->second);
widget->UpdateFromMRML(this->SliceNode, vtkCommand::ModifiedEvent);
widget->UpdateFromMRML(viewNode, vtkCommand::ModifiedEvent);
if (widget->GetNeedToRender())
{
renderRequested = true;
Expand All @@ -501,7 +501,7 @@ void vtkMRMLMarkupsDisplayableManager::OnMRMLSliceNodeModifiedEvent()
{
// we loop through all widgets
vtkSlicerMarkupsInteractionWidget* interactionWidget = (interactionIt->second);
interactionWidget->UpdateFromMRML(this->SliceNode, vtkCommand::ModifiedEvent);
interactionWidget->UpdateFromMRML(viewNode, vtkCommand::ModifiedEvent);
if (interactionWidget->GetNeedToRender())
{
renderRequested = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ class VTK_SLICER_MARKUPS_MODULE_MRMLDISPLAYABLEMANAGER_EXPORT vtkMRMLMarkupsDis
/// Called after the corresponding MRML View container was modified
void OnMRMLDisplayableNodeModifiedEvent(vtkObject* caller) override;

/// Handler for specific SliceView actions, iterate over the widgets in the helper
virtual void OnMRMLSliceNodeModifiedEvent();
/// Update all widgets in response to view node modification
virtual void OnMRMLViewNodeModifiedEvent(vtkMRMLAbstractViewNode* viewNode);

/// Observe the interaction node.
void AddObserversToInteractionNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ void vtkSlicerCurveRepresentation3D::CanInteractWithCurve(
this->CurvePointLocator->FindClosestPoint(worldPosition, closestPointDisplay, cellId, subId, dist2);
}

if (dist2>=0 && dist2 < this->ControlPointSize + this->PickingTolerance * this->ScreenScaleFactor * this->ViewScaleFactorMmPerPixel)
if (dist2>=0 && dist2 < this->ControlPointSize + this->PickingTolerance * this->GetScreenScaleFactor() * this->ViewScaleFactorMmPerPixel)
{
closestDistance2 = dist2 / this->ViewScaleFactorMmPerPixel / this->ViewScaleFactorMmPerPixel;
foundComponentType = vtkMRMLMarkupsDisplayNode::ComponentLine;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ int vtkSlicerMarkupsWidgetRepresentation::FindClosestPointOnWidget(
this->Renderer->DisplayToWorld();
this->Renderer->GetWorldPoint(tmp2);

tmp1[0] = this->PickingTolerance * this->ScreenScaleFactor;
tmp1[0] = this->PickingTolerance * this->GetScreenScaleFactor();
this->Renderer->SetDisplayPoint(tmp1);
this->Renderer->DisplayToWorld();
this->Renderer->GetWorldPoint(tmp1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ void vtkSlicerMarkupsWidgetRepresentation2D::UpdateFromMRMLInternal(vtkMRMLNode*
controlPoints->TextProperty->SetColor(color);
controlPoints->TextProperty->SetOpacity(opacity);
controlPoints->TextProperty->SetFontSize(static_cast<int>(this->MarkupsDisplayNode->GetTextProperty()->GetFontSize()
* this->MarkupsDisplayNode->GetTextScale()));
* this->MarkupsDisplayNode->GetTextScale() * this->GetScreenScaleFactor() * 5.0));
controlPoints->TextProperty->SetBackgroundOpacity(opacity * this->MarkupsDisplayNode->GetTextProperty()->GetBackgroundOpacity());

vtkMarkupsGlyphSource2D* glyphSource = this->GetControlPointsPipeline(controlPointType)->GlyphSource2D;
Expand Down Expand Up @@ -499,7 +499,7 @@ void vtkSlicerMarkupsWidgetRepresentation2D::UpdateFromMRMLInternal(vtkMRMLNode*
}

// put the labels near the boundary of the glyph, slightly away from it (by half picking tolarance)
double labelsOffset = this->ControlPointSize * 0.5 + this->PickingTolerance * 0.5 * this->ScreenScaleFactor;
double labelsOffset = this->ControlPointSize * 0.5 + this->PickingTolerance * 0.5 * this->GetScreenScaleFactor();
this->UpdateAllPointsAndLabelsFromMRML(labelsOffset);

this->VisibilityOn();
Expand Down Expand Up @@ -1162,7 +1162,7 @@ void vtkSlicerMarkupsWidgetRepresentation2D::UpdateControlPointSize()
if (this->MarkupsDisplayNode->GetUseGlyphScale())
{
// relative
this->ControlPointSize = this->ScreenSizePixel * this->ScreenScaleFactor * this->MarkupsDisplayNode->GetGlyphScale() / 100.0;
this->ControlPointSize = this->ScreenSizePixel * this->GetScreenScaleFactor() * this->MarkupsDisplayNode->GetGlyphScale() / 100.0;
}
else
{
Expand All @@ -1174,7 +1174,7 @@ void vtkSlicerMarkupsWidgetRepresentation2D::UpdateControlPointSize()
//----------------------------------------------------------------------
double vtkSlicerMarkupsWidgetRepresentation2D::GetMaximumControlPointPickingDistance2()
{
double maximumControlPointPickingDistance = this->ControlPointSize / 2.0 + this->PickingTolerance * this->ScreenScaleFactor;
double maximumControlPointPickingDistance = this->ControlPointSize / 2.0 + this->PickingTolerance * this->GetScreenScaleFactor();
return maximumControlPointPickingDistance * maximumControlPointPickingDistance;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ void vtkSlicerMarkupsWidgetRepresentation3D::CanInteract(
if (interactionEventData->IsDisplayPositionValid())
{
double pixelTolerance = this->ControlPointSize / 2.0 / this->GetViewScaleFactorAtPosition(centerPosWorld, interactionEventData)
+ this->PickingTolerance * this->ScreenScaleFactor;
+ this->PickingTolerance * this->GetScreenScaleFactor();
interactionEventData->WorldToDisplay(centerPosWorld, centerPosDisplay);
double dist2 = vtkMath::Distance2BetweenPoints(centerPosDisplay, displayPosition3);
if (dist2 < pixelTolerance * pixelTolerance)
Expand Down Expand Up @@ -461,7 +461,7 @@ void vtkSlicerMarkupsWidgetRepresentation3D::CanInteract(
if (interactionEventData->IsDisplayPositionValid())
{
double pixelTolerance = this->ControlPointSize / 2.0 / this->GetViewScaleFactorAtPosition(centerPosWorld, interactionEventData)
+ this->PickingTolerance * this->ScreenScaleFactor;
+ this->PickingTolerance * this->GetScreenScaleFactor();
interactionEventData->WorldToDisplay(centerPosWorld, centerPosDisplay);
double dist2 = vtkMath::Distance2BetweenPoints(centerPosDisplay, displayPosition3);
if (dist2 < pixelTolerance * pixelTolerance && dist2 < closestDistance2)
Expand Down Expand Up @@ -582,7 +582,7 @@ void vtkSlicerMarkupsWidgetRepresentation3D::UpdateFromMRMLInternal(vtkMRMLNode*
controlPoints->TextProperty->SetColor(color);
controlPoints->TextProperty->SetOpacity(opacity);
controlPoints->TextProperty->SetFontSize(static_cast<int>(this->MarkupsDisplayNode->GetTextProperty()->GetFontSize()
* this->MarkupsDisplayNode->GetTextScale()));
* this->MarkupsDisplayNode->GetTextScale() * this->GetScreenScaleFactor() * 5.0));
controlPoints->TextProperty->SetBackgroundOpacity(opacity * this->MarkupsDisplayNode->GetTextProperty()->GetBackgroundOpacity());

controlPoints->OccludedProperty->SetColor(color);
Expand Down Expand Up @@ -855,7 +855,7 @@ int vtkSlicerMarkupsWidgetRepresentation3D::RenderOpaqueGeometry(
{
updateControlPointSize = true;
}
newControlPointSize = this->ScreenSizePixel * this->ScreenScaleFactor
newControlPointSize = this->ScreenSizePixel * this->GetScreenScaleFactor()
* this->MarkupsDisplayNode->GetGlyphScale() / 100.0 * this->ViewScaleFactorMmPerPixel;
// Only update the size if there is noticeable difference to avoid slight flickering
// when the camera is moved
Expand Down Expand Up @@ -1231,7 +1231,7 @@ void vtkSlicerMarkupsWidgetRepresentation3D::UpdateControlPointSize()
{
if (this->MarkupsDisplayNode->GetUseGlyphScale())
{
this->ControlPointSize = this->ScreenSizePixel * this->ScreenScaleFactor
this->ControlPointSize = this->ScreenSizePixel * this->GetScreenScaleFactor()
* this->MarkupsDisplayNode->GetGlyphScale() / 100.0 * this->ViewScaleFactorMmPerPixel;
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ void vtkSlicerPlaneRepresentation2D::CanInteractWithPlane(
double closestPointDisplay[3] = { 0.0, 0.0, 0.0 };
this->GetWorldToSliceCoordinates(closestPointWorld, closestPointDisplay);

double pixelTolerance = this->PickingTolerance * this->ScreenScaleFactor;
double pixelTolerance = this->PickingTolerance * this->GetScreenScaleFactor();
const int* displayPosition = interactionEventData->GetDisplayPosition();
double displayPosition3[3] = { static_cast<double>(displayPosition[0]), static_cast<double>(displayPosition[1]), 0.0 };
double dist2Display = vtkMath::Distance2BetweenPoints(displayPosition3, closestPointDisplay);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ void vtkSlicerPlaneRepresentation3D::CanInteractWithPlane(
this->Renderer->GetWorldPoint(closestPointWorld);

double pixelTolerance = this->PlaneOutlineFilter->GetRadius() / 2.0 / this->GetViewScaleFactorAtPosition(closestPointWorld)
+ this->PickingTolerance * this->ScreenScaleFactor;
+ this->PickingTolerance * this->GetScreenScaleFactor();
if (dist2Display < pixelTolerance * pixelTolerance && dist2Display < closestDistance2)
{
closestDistance2 = dist2Display;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ void vtkSlicerROIRepresentation2D::CanInteractWithROI(
double closestPointDisplay[3] = { 0.0, 0.0, 0.0 };
this->GetWorldToSliceCoordinates(closestPointWorld, closestPointDisplay);

double pixelTolerance = this->PickingTolerance * this->ScreenScaleFactor;
double pixelTolerance = this->PickingTolerance * this->GetScreenScaleFactor();
const int* displayPosition = interactionEventData->GetDisplayPosition();
double displayPosition3[3] = { static_cast<double>(displayPosition[0]), static_cast<double>(displayPosition[1]), 0.0 };
double dist2Display = vtkMath::Distance2BetweenPoints(displayPosition3, closestPointDisplay);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ void vtkSlicerROIRepresentation3D::CanInteractWithROI(
}
}

double pixelTolerance = this->PickingTolerance * this->ScreenScaleFactor;
double pixelTolerance = this->PickingTolerance * this->GetScreenScaleFactor();
if (distance2Display < VTK_DOUBLE_MAX && distance2Display < pixelTolerance * pixelTolerance && distance2Display < closestDistance2)
{
closestDistance2 = distance2Display;
Expand Down

0 comments on commit 500ea52

Please sign in to comment.