From 193c3a18720636082d68836124f96bb62a93129a Mon Sep 17 00:00:00 2001 From: Nicole Aucoin Date: Thu, 15 Nov 2012 19:40:38 -0500 Subject: [PATCH 1/2] BUG: issue #1690 starting to fix annotations in light box mode. Use the renderer from the light box proxy manager if it's defined BUG: when in light box mode, switch to using 2d point handles for fiducials. This works for placing new ones in limited testing, but not to convert existing ones to 2d handles yet. Issue #1690 BUG: switch fiducials between 2d and 3d as swap between light box and not. TODO: initiate the change when the lightbox mode is changed, as opposed to when move the fiducial in 3d. issue #1690 BUG: update fiducial widgets on slice node changes. If just update position, the handle widget 2d/3d type won't get updated when switch in and out of light box mode. This may slow things down a bit, but they'll be more correct. Issue #1690 BUG: Avoid seed widget and representation having different renderers on creation. Check the light box state so set the correct renderer on creation. Issue # 1690 Check that the seed representation has a valid renderer and active camera before trying to set display position, should fix load crash with issue #2512 BUG: unify light box index calculation. Try to use the volume slice spacing to determine the light box. TBD: make sure taking the correct element of the spacing array. Issue #1690 BUG: fix showing annotations in 0th light box. This should fix the annotations showing up in the 0th light box when the annotation should be in the -1th. Use floor rather than a cast to int to convert from floating point display coordinate to light box index to avoid rounding toward 0.0 in negative space. Issue #1690 BUG: take into account volume spacing when deciding if should show a fiducial in a light box. Calculate the distance between the fiducial and the slice at this light box index using distance from the fiducial point and the slice plane. Issue #1690 BUG: try to get the point handles to show up when going into light box mode. Delete and recreate seed widget when swapping in and out of light box mode, still work to do. Issue #1690 STYLE: code clean up. Change warnings to debugs, expand comments, fix a comparison to null. Issue #1690 STYLE: update comment on AddAnnotation to reflect implementation Issue #1690 BUG: reset the glyph type when changing in and out of lightbox mode. Needed to clear out the entry in the NodeGlyphTypes map so that the glyph would get updated on re-creation. Issue #1690 --- .../vtkMRMLAnnotationDisplayableManager.cxx | 338 +++++++++++------- .../vtkMRMLAnnotationDisplayableManager.h | 12 + ...MLAnnotationFiducialDisplayableManager.cxx | 163 ++++++++- 3 files changed, 373 insertions(+), 140 deletions(-) diff --git a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx index 4470c6a5e..c789fa4f4 100644 --- a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx +++ b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx @@ -262,24 +262,11 @@ void vtkMRMLAnnotationDisplayableManager::UpdateFromMRML() // do we have a widget for it? if (this->GetWidget(annotationNode) == NULL) { - vtkDebugMacro("UpdateFromMRML: creating a widget for node " << annotationNode->GetID()); - vtkAbstractWidget *widget = this->CreateWidget(annotationNode); - if (widget) + vtkDebugMacro("UpdateFromMRML: adding node " << annotationNode->GetID()); + if (this->AddAnnotation(annotationNode)) { - // Add widget to the list - this->Helper->Widgets[annotationNode] = widget; - - // Add the node to the list. - this->Helper->AnnotationNodeList.push_back(annotationNode); - - // Refresh observers - this->SetAndObserveNode(annotationNode); - - // tear down widget creation - this->OnWidgetCreated(widget, annotationNode); - // update the new widget from the node - this->PropagateMRMLToWidget(annotationNode, widget); + this->PropagateMRMLToWidget(annotationNode, this->GetWidget(annotationNode)); } } } @@ -418,47 +405,12 @@ void vtkMRMLAnnotationDisplayableManager::OnMRMLSceneNodeAdded(vtkMRMLNode* node vtkDebugMacro("OnMRMLSceneNodeAddedEvent: node " << node->GetID()); - // Node added should not be already managed - vtkMRMLAnnotationDisplayableManagerHelper::AnnotationNodeListIt it = std::find( - this->Helper->AnnotationNodeList.begin(), - this->Helper->AnnotationNodeList.end(), - annotationNode); - if (it != this->Helper->AnnotationNodeList.end()) - { - vtkErrorMacro("OnMRMLSceneNodeAddedEvent: This node is already associated to the displayable manager!") - return; - } - - // There should not be a widget for the new node - if (this->Helper->GetWidget(annotationNode) != 0) + if (!this->AddAnnotation(annotationNode)) { - vtkErrorMacro("OnMRMLSceneNodeAddedEvent: A widget is already associated to this node!"); + vtkErrorMacro("OnMRMLSceneNodeAddedEvent: failed to add annotation node " << node->GetName()); return; } - //std::cout << "OnMRMLSceneNodeAddedEvent ThreeD -> CreateWidget" << std::endl; - - // Create the Widget and add it to the list. - vtkAbstractWidget* newWidget = this->CreateWidget(annotationNode); - if (!newWidget) - { - vtkErrorMacro("OnMRMLSceneNodeAddedEvent: Widget was not created!") - return; - } - this->Helper->Widgets[annotationNode] = newWidget; - - // Add the node to the list. - this->Helper->AnnotationNodeList.push_back(annotationNode); - - // Refresh observers - this->SetAndObserveNode(annotationNode); - - // TODO do we need this render call? - this->RequestRender(); - - // tear down widget creation - this->OnWidgetCreated(newWidget, annotationNode); - // Remove all placed seeds this->Helper->RemoveSeeds(); @@ -736,8 +688,10 @@ void vtkMRMLAnnotationDisplayableManager::OnMRMLSliceNodeModifiedEvent(vtkMRMLSl if (visibleOnSlice) { - // it's visible, just update the position - this->UpdatePosition(this->Helper->GetWidget(annotationNode), annotationNode); + // it's visible, but if just update the position, don't get updates + //necessary when switch into and out of lightbox + vtkDebugMacro("OnMRMLSliceNodeModifiedEvent: visible, propagate mrml to widget"); + this->PropagateMRMLToWidget(annotationNode, this->Helper->GetWidget(annotationNode)); } else @@ -890,15 +844,12 @@ bool vtkMRMLAnnotationDisplayableManager::IsWidgetDisplayable(vtkMRMLSliceNode* if (this->IsInLightboxMode()) { - /// BUG mantis issue 1690: if in lightbox mode, don't show fiducials or - /// rulers - if (!strcmp(this->m_Focus, "vtkMRMLAnnotationFiducialNode") || - !strcmp(this->m_Focus, "vtkMRMLAnnotationRulerNode")) + /// BUG mantis issue 1690: if in lightbox mode, don't show rulers + if (!strcmp(this->m_Focus, "vtkMRMLAnnotationRulerNode")) { return false; } } - int numberOfControlPoints = controlPointsNode->GetNumberOfControlPoints(); // the text node saves it's second control point in viewport coordinates, so @@ -926,7 +877,7 @@ bool vtkMRMLAnnotationDisplayableManager::IsWidgetDisplayable(vtkMRMLSliceNode* // get the corresponding lightbox index for this display coordinate and // check if it's in the range of the current number of light boxes being // displayed in the grid rows/columns. - int lightboxIndex = (int)(displayCoordinates[2]+0.5); + int lightboxIndex = this->GetLightboxIndex(controlPointsNode); int numberOfLightboxes = sliceNode->GetLayoutGridColumns() * sliceNode->GetLayoutGridRows(); //std::cout << "IsWidgetDisplayable: " << sliceNode->GetName() << ": lightbox mode, index = " << lightboxIndex << ", rows = " << sliceNode->GetLayoutGridRows() << ", cols = " << sliceNode->GetLayoutGridColumns() << ", number of light boxes = " << numberOfLightboxes << std::endl; if (lightboxIndex < 0 || @@ -937,84 +888,128 @@ bool vtkMRMLAnnotationDisplayableManager::IsWidgetDisplayable(vtkMRMLSliceNode* else { // get the right renderer index by checking the z coordinate - int rendererIndex = lightboxIndex; - - // get all renderers associated with this renderWindow - // when lightbox mode is enabled, there will be different renderers - // associated with the renderWindow of the sliceView (there may also - // be more renderers than light boxes) - vtkRendererCollection* rendererCollection = this->GetInteractor()->GetRenderWindow()->GetRenderers(); - - // check if the rendererIndex is valid for the current lightbox view - if (rendererIndex >= 0 && rendererIndex < rendererCollection->GetNumberOfItems()) + vtkRenderer* currentRenderer = this->GetRenderer(lightboxIndex); + + // now we get the widget.. + vtkAbstractWidget* widget = this->GetWidget(node); + + // TODO this code blocks the movement of the widget in lightbox mode + if (widget && + (widget->GetCurrentRenderer() != currentRenderer || + widget->GetRepresentation()->GetRenderer() != currentRenderer)) { - - vtkRenderer* currentRenderer = vtkRenderer::SafeDownCast(rendererCollection->GetItemAsObject(rendererIndex)); - - // now we get the widget.. - vtkAbstractWidget* widget = this->GetWidget(node); - - // TODO this code blocks the movement of the widget in lightbox mode - if (widget && - (widget->GetCurrentRenderer() != currentRenderer || - widget->GetRepresentation()->GetRenderer() != currentRenderer)) + vtkDebugMacro("IsWidgetDisplayable: updating renderer on widget and representation"); + // if the widget is on, need to turn it off to set the renderer + bool toggleOffOn = false; + if (widget->GetEnabled()) { - // if the widget is on, need to turn it off to set the renderer - bool toggleOffOn = false; - if (widget->GetEnabled()) - { - // turn it off.. - widget->Off(); - toggleOffOn = true; - } - // ..place it and its representation to the right renderer.. - widget->SetCurrentRenderer(currentRenderer); - widget->GetRepresentation()->SetRenderer(currentRenderer); - if (toggleOffOn) - { - // ..and turn it on again! - widget->On(); - } - // if it's a seed widget, go to complete interaction state - vtkSeedWidget *seedWidget = vtkSeedWidget::SafeDownCast(widget); - if (seedWidget) - { - vtkDebugMacro("SeedWidget: Complete interaction"); - seedWidget->CompleteInteraction(); - } + // turn it off.. + widget->Off(); + toggleOffOn = true; + } + // ..place it and its representation to the right renderer.. + + widget->SetCurrentRenderer(currentRenderer); + widget->GetRepresentation()->SetRenderer(currentRenderer); + + if (toggleOffOn) + { + // ..and turn it on again! + widget->On(); + } + // if it's a seed widget, go to complete interaction state + vtkSeedWidget *seedWidget = vtkSeedWidget::SafeDownCast(widget); + if (seedWidget) + { + vtkDebugMacro("SeedWidget: Complete interaction"); + seedWidget->CompleteInteraction(); + } - // we need to render again - if (currentRenderer) - { - currentRenderer->Render(); - } + // we need to render again + if (currentRenderer) + { + currentRenderer->Render(); } } - else - { - // it's out of range of the current collection of renderers - showWidget = false; - } + } // // End of Lightbox specific code // } - else + + // check if the annotation is close enough to the slice to be shown + if (showWidget) { - // the third coordinate of the displayCoordinates is the distance to the slice - float distanceToSlice = displayCoordinates[2]; - float maxDistance = 0.5 + (sliceNode->GetDimensions()[2] - 1); - if (distanceToSlice < -0.5 || distanceToSlice >= maxDistance) + if (this->IsInLightboxMode()) { - // if the distance to the slice is more than 0.5mm, we know that at least one coordinate of the widget is outside the current activeSlice - // hence, we do not want to show this widget - showWidget = false; - // we don't even need to continue parsing the controlpoints, because we know the widget will not be shown - break; + // get the volume's spacing to determine the distance between the slice + // location and the annotation + // default to spacing 1.0 in case can't get volume slice spacing from + // the logic as that will be a multiplicative no-op + double spacing = 1.0; + vtkMRMLSliceLogic *sliceLogic = NULL; + vtkMRMLApplicationLogic *mrmlAppLogic = this->GetMRMLApplicationLogic(); + if (mrmlAppLogic) + { + sliceLogic = mrmlAppLogic->GetSliceLogic(this->GetSliceNode()); + } + if (sliceLogic) + { + double *volumeSliceSpacing = sliceLogic->GetLowestVolumeSliceSpacing(); + if (volumeSliceSpacing != NULL) + { + vtkDebugMacro("Slice node " << this->GetSliceNode()->GetName() << ": volumeSliceSpacing = " << volumeSliceSpacing[0] << ", " << volumeSliceSpacing[1] << ", " << volumeSliceSpacing[2]); + spacing = volumeSliceSpacing[2]; + } + } + vtkDebugMacro("displayCoordinates: " << displayCoordinates[0] << "," << displayCoordinates[1] << "," << displayCoordinates[2] << "\n\tworld coords: " << transformedWorldCoordinates[0] << "," << transformedWorldCoordinates[1] << "," << transformedWorldCoordinates[2]); + // calculate the distance from the annotation in world space to the + // plane defined by the slice node normal and origin (using same + // convention as the vtkMRMLThreeDReformatDisplayableManager) + vtkMatrix4x4 *sliceToRAS = this->GetSliceNode()->GetSliceToRAS(); + double slicePlaneNormal[3], slicePlaneOrigin[3]; + slicePlaneNormal[0] = sliceToRAS->GetElement(0,2); + slicePlaneNormal[1] = sliceToRAS->GetElement(1,2); + slicePlaneNormal[2] = sliceToRAS->GetElement(2,2); + slicePlaneOrigin[0] = sliceToRAS->GetElement(0,3); + slicePlaneOrigin[1] = sliceToRAS->GetElement(1,3); + slicePlaneOrigin[2] = sliceToRAS->GetElement(2,3); + double distanceToPlane = slicePlaneNormal[0]*(transformedWorldCoordinates[0]-slicePlaneOrigin[0]) + + slicePlaneNormal[1]*(transformedWorldCoordinates[1]-slicePlaneOrigin[1]) + + slicePlaneNormal[2]*(transformedWorldCoordinates[2]-slicePlaneOrigin[2]); + // this gives the distance to light box plane 0, but have to offset by + // number of light box planes (as determined by the light box index) times the volume + // slice spacing + int lightboxIndex = this->GetLightboxIndex(controlPointsNode); + double lightboxOffset = lightboxIndex * spacing; + double distanceToSlice = distanceToPlane - lightboxOffset; + double maxDistance = 0.5; + vtkDebugMacro("\n\tdistance to plane = " << distanceToPlane << "\n\tlightboxIndex = " << lightboxIndex << "\n\tlightboxOffset = " << lightboxOffset << "\n\tdistance to slice = " << distanceToSlice); + // check that it's within 0.5mm + if (distanceToSlice < -0.5 || distanceToSlice >= maxDistance) + { + vtkDebugMacro("Distance to slice is greater than max distance, not showing the widget"); + showWidget = false; + break; + } + } + else + { + // the third coordinate of the displayCoordinates is the distance to the slice + float distanceToSlice = displayCoordinates[2]; + float maxDistance = 0.5 + (sliceNode->GetDimensions()[2] - 1); + vtkDebugMacro("Slice node " << this->GetSliceNode()->GetName() << ": distance to slice = " << distanceToSlice << ", maxDistance = " << maxDistance << "\n\tslice node dimenions[2] = " << sliceNode->GetDimensions()[2]); + if (distanceToSlice < -0.5 || distanceToSlice >= maxDistance) + { + // if the distance to the slice is more than 0.5mm, we know that at least one coordinate of the widget is outside the current activeSlice + // hence, we do not want to show this widget + showWidget = false; + // we don't even need to continue parsing the controlpoints, because we know the widget will not be shown + break; + } } } - // ----------------------------------------- // special cases when the slices get panned: @@ -1623,3 +1618,96 @@ bool vtkMRMLAnnotationDisplayableManager::IsInLightboxMode() return flag; } + + +//--------------------------------------------------------------------------- +int vtkMRMLAnnotationDisplayableManager::GetLightboxIndex(vtkMRMLAnnotationNode *node, int controlPointIndex) +{ + int index = -1; + + if (!node) + { + return index; + } + if (!this->IsInLightboxMode()) + { + return index; + } + + // down cast the node as a controlpoints node to get the coordinates + vtkMRMLAnnotationControlPointsNode * controlPointsNode = vtkMRMLAnnotationControlPointsNode::SafeDownCast(node); + + if (!controlPointsNode) + { + vtkErrorMacro("GetLightboxIndex: Could not get the controlpoints node.") + return index; + } + + if (controlPointIndex < 0 || + controlPointIndex >= controlPointsNode->GetNumberOfControlPoints()) + { + return index; + } + + double transformedWorldCoordinates[4]; + controlPointsNode->GetControlPointWorldCoordinates(controlPointIndex, transformedWorldCoordinates); + double displayCoordinates[4]; + this->GetWorldToDisplayCoordinates(transformedWorldCoordinates,displayCoordinates); + + index = (int)(floor(displayCoordinates[2]+0.5)); + + + return index; +} + +//--------------------------------------------------------------------------- +bool vtkMRMLAnnotationDisplayableManager::AddAnnotation(vtkMRMLAnnotationNode *annotationNode) +{ + if (!annotationNode || !this->GetMRMLScene()) + { + return false; + } + + // Node added should not be already managed + vtkMRMLAnnotationDisplayableManagerHelper::AnnotationNodeListIt it = std::find( + this->Helper->AnnotationNodeList.begin(), + this->Helper->AnnotationNodeList.end(), + annotationNode); + if (it != this->Helper->AnnotationNodeList.end()) + { + vtkErrorMacro("AddAnnotation: This node is already associated to the displayable manager!"); + return false; + } + + // There should not be a widget for the new node + if (this->Helper->GetWidget(annotationNode) != 0) + { + vtkErrorMacro("AddAnnotation: A widget is already associated to this node!"); + return false; + } + + vtkDebugMacro("AddAnnotation: ThreeD -> CreateWidget"); + + // Create the Widget and add it to the list. + vtkAbstractWidget* newWidget = this->CreateWidget(annotationNode); + if (!newWidget) + { + vtkErrorMacro("AddAnnotation: Widget was not created!") + return false; + } + this->Helper->Widgets[annotationNode] = newWidget; + + // Add the node to the list. + this->Helper->AnnotationNodeList.push_back(annotationNode); + + // Refresh observers + this->SetAndObserveNode(annotationNode); + + // TODO do we need this render call? + this->RequestRender(); + + // tear down widget creation + this->OnWidgetCreated(newWidget, annotationNode); + + return true; +} diff --git a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.h b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.h index 4b5a89f8c..8e2cb2f18 100644 --- a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.h +++ b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.h @@ -88,6 +88,18 @@ public: /// grid columns or rows > 1 bool IsInLightboxMode(); + /// Gets the world coordinate of the annotation node, transforms it to + /// display coordinates. + /// Defaults to returning the 0th control point's light box index. Returns + /// -1 if not in lightbox mode. + int GetLightboxIndex(vtkMRMLAnnotationNode *node, int controlPointIndex = 0); + + /// Set up data structures for an annotation node. Returns false on failure + /// or if it's already set up. Can be called to reinitialise a node's widgets + /// after calling RemoveWidgetAndNode on the Helper + /// \sa vtkMRMLAnnotationDisplayableManagerHelper::RemoveWidgetAndNode() + bool AddAnnotation(vtkMRMLAnnotationNode *node); + protected: vtkMRMLAnnotationDisplayableManager(); diff --git a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationFiducialDisplayableManager.cxx b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationFiducialDisplayableManager.cxx index 245959bad..595cd7336 100644 --- a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationFiducialDisplayableManager.cxx +++ b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationFiducialDisplayableManager.cxx @@ -104,7 +104,11 @@ public: // only if we had to restrict the coordinates aka. if the coordinates changed, we update the positions if (this->m_DisplayableManager->GetDisplayCoordinatesChanged(displayCoordinates1,restrictedDisplayCoordinates1)) { - representation->SetSeedDisplayPosition(0,restrictedDisplayCoordinates1); + if (representation->GetRenderer() && + representation->GetRenderer()->GetActiveCamera()) + { + representation->SetSeedDisplayPosition(0,restrictedDisplayCoordinates1); + } } } @@ -180,18 +184,35 @@ vtkAbstractWidget * vtkMRMLAnnotationFiducialDisplayableManager::CreateWidget(vt std::cout<<"No DisplayNode!"<::iterator iter = this->NodeGlyphTypes.find(displayNode); + if (iter != this->NodeGlyphTypes.end()) + { + vtkDebugMacro("CreateWidget: found a glyph type already defined for this node: " << iter->second); + this->NodeGlyphTypes[displayNode] = vtkMRMLAnnotationPointDisplayNode::GlyphMin - 1; + } vtkNew rep; - vtkNew handle; - - // default to a starburst glyph, update in propagate mrml to widget - vtkNew glyphSource; - glyphSource->SetGlyphType(vtkMRMLAnnotationPointDisplayNode::StarBurst2D); - glyphSource->Update(); - glyphSource->SetScale(1.0); - handle->SetHandle(glyphSource->GetOutput()); + if (!this->IsInLightboxMode()) + { + vtkDebugMacro("CreateWidget: not in light box mode, making a 3d handle"); + vtkNew handle; + // default to a sphere glyph, update in propagate mrml to widget + vtkNew glyphSource; + glyphSource->SetGlyphType(vtkMRMLAnnotationPointDisplayNode::Sphere3D); + glyphSource->Update(); + glyphSource->SetScale(1.0); + handle->SetHandle(glyphSource->GetOutput()); + rep->SetHandleRepresentation(handle.GetPointer()); + } + else + { + vtkDebugMacro("CreateWidget: in light box mode, making a 2d handle"); + vtkNew handle; + rep->SetHandleRepresentation(handle.GetPointer()); + } - rep->SetHandleRepresentation(handle.GetPointer()); + //seed widget @@ -201,7 +222,18 @@ vtkAbstractWidget * vtkMRMLAnnotationFiducialDisplayableManager::CreateWidget(vt seedWidget->SetRepresentation(rep.GetPointer()); seedWidget->SetInteractor(this->GetInteractor()); - seedWidget->SetCurrentRenderer(this->GetRenderer()); + // set the renderer on the widget and representation + if (!this->IsInLightboxMode()) + { + seedWidget->SetCurrentRenderer(this->GetRenderer()); + seedWidget->GetRepresentation()->SetRenderer(this->GetRenderer()); + } + else + { + int lightboxIndex = this->GetLightboxIndex(fiducialNode); + seedWidget->SetCurrentRenderer(this->GetRenderer(lightboxIndex)); + seedWidget->GetRepresentation()->SetRenderer(this->GetRenderer(lightboxIndex)); + } //seedWidget->ProcessEventsOff(); @@ -297,7 +329,7 @@ void vtkMRMLAnnotationFiducialDisplayableManager::PropagateMRMLToWidget(vtkMRMLA vtkErrorMacro("PropagateMRMLToWidget: Could not get seed widget!") return; } - + // cast to the specific mrml node vtkMRMLAnnotationFiducialNode* fiducialNode = vtkMRMLAnnotationFiducialNode::SafeDownCast(node); @@ -312,7 +344,11 @@ void vtkMRMLAnnotationFiducialDisplayableManager::PropagateMRMLToWidget(vtkMRMLA // now get the widget properties (coordinates, measurement etc.) and if the mrml node has changed, propagate the changes vtkSeedRepresentation * seedRepresentation = vtkSeedRepresentation::SafeDownCast(seedWidget->GetRepresentation()); - + if (!seedRepresentation) + { + vtkErrorMacro("PropagateMRMLToWidget: Could not get seed representation from widget!") + return; + } vtkMRMLAnnotationPointDisplayNode *displayNode = fiducialNode->GetAnnotationPointDisplayNode(); @@ -322,6 +358,54 @@ void vtkMRMLAnnotationFiducialDisplayableManager::PropagateMRMLToWidget(vtkMRMLA } vtkOrientedPolygonalHandleRepresentation3D *handleRep = vtkOrientedPolygonalHandleRepresentation3D::SafeDownCast(seedRepresentation->GetHandleRepresentation(0)); + // might be in lightbox mode where using a 2d point handle + vtkPointHandleRepresentation2D *pointHandleRep = vtkPointHandleRepresentation2D::SafeDownCast(seedRepresentation->GetHandleRepresentation(0)); + // double check that if switch in and out of light box mode, the handle rep + // is updated + bool updateHandleType = false; + if (this->IsInLightboxMode()) + { + if (handleRep) + { + vtkDebugMacro("PropagateMRMLToWidget: have a 3d handle representation in 2d light box, resetting it."); + updateHandleType = true; + } + } + else + { + if (pointHandleRep) + { + vtkDebugMacro("PropagateMRMLToWidget: Not in light box, but have a point handle."); + updateHandleType = true; + } + } + if (updateHandleType) + { + vtkDebugMacro("PropagateMRMLToWidget: removing widget..."); + // clean it out + this->Helper->RemoveWidgetAndNode(node); + // recreate it + vtkMRMLAnnotationNode *annotationNode = vtkMRMLAnnotationNode::SafeDownCast(node); + if (annotationNode) + { + this->AddAnnotation(annotationNode); + } + // did it come back in here + vtkDebugMacro("PropagateMRMLToWidget: did it end up calling this method already?"); + // probably not, so call and return + vtkAbstractWidget * widget = this->Helper->GetWidget(annotationNode); + if (widget) + { + this->PropagateMRMLToWidget(annotationNode, widget); + } + else + { + vtkWarningMacro("PropagateMRMLToWidget: failed to add a new widget for node " << node->GetName()); + return; + } + vtkDebugMacro("PropagateMRMLToWidget: NOW returning after calling self from self"); + return; + } if (handleRep) { if (displayNode) @@ -464,7 +548,50 @@ void vtkMRMLAnnotationFiducialDisplayableManager::PropagateMRMLToWidget(vtkMRMLA handleRep->LabelVisibilityOff(); } }//if (handleRep) - + else if (pointHandleRep) + { + if (displayNode) + { + // glyph type + /* + std::map::iterator iter = this->NodeGlyphTypes.find(displayNode); + if (iter == this->NodeGlyphTypes.end() || iter->second != displayNode->GetGlyphType()) + { + // map the 3d sphere to a filled circle, the 3d diamond to a filled + // diamond + vtkNew glyphSource; + if (displayNode->GetGlyphType() == vtkMRMLAnnotationPointDisplayNode::Sphere3D) + { + glyphSource->SetGlyphType(vtkMRMLAnnotationPointDisplayNode::Circle2D); + } + else if (displayNode->GetGlyphType() == vtkMRMLAnnotationPointDisplayNode::Diamond3D) + { + glyphSource->SetGlyphType(vtkMRMLAnnotationPointDisplayNode::Diamond2D); + } + else + { + glyphSource->SetGlyphType(displayNode->GetGlyphType()); + } + glyphSource->Update(); + glyphSource->SetScale(1.0); + std::cout << "PropagateMRMLToWidget: " << this->GetSliceNode()->GetName() << ": setting point handle rep cursor shape " << glyphSource->GetOutput() << std::endl; + pointHandleRep->SetCursorShape(glyphSource->GetOutput()); + this->NodeGlyphTypes[displayNode] = displayNode->GetGlyphType(); + } + */ + // set the color + if (fiducialNode->GetSelected()) + { + // use the selected color + pointHandleRep->GetProperty()->SetColor(displayNode->GetSelectedColor()); + } + else + { + // use the unselected color + pointHandleRep->GetProperty()->SetColor(displayNode->GetColor()); + } + } + } // now update the position this->UpdatePosition(widget, node); @@ -762,7 +889,13 @@ void vtkMRMLAnnotationFiducialDisplayableManager::UpdatePosition(vtkAbstractWidg { // only update when really changed vtkDebugMacro("UpdatePosition: " << this->GetSliceNode()->GetName() << ": display coordinates changed:\n\tseed display = " << displayCoordinatesBuffer1[0] << ", " << displayCoordinatesBuffer1[1] << "\n\tfid display = " << displayCoordinates1[0] << ", " << displayCoordinates1[1] ); - seedRepresentation->SetSeedDisplayPosition(0,displayCoordinates1); + // make sure the representation has a renderer and an active camera to + // avoid a crash in vtkRenderer::ViewToWorld + if (seedRepresentation->GetRenderer() && + seedRepresentation->GetRenderer()->GetActiveCamera()) + { + seedRepresentation->SetSeedDisplayPosition(0,displayCoordinates1); + } positionChanged = true; } else -- GitLab From e0d1a06d0957f819407370b6de0713a1f173165c Mon Sep 17 00:00:00 2001 From: Nicole Aucoin Date: Thu, 15 Nov 2012 19:40:38 -0500 Subject: [PATCH 2/2] BUG: Re-enable and fix fiducial display in light box mode. Use the renderer from the light box proxy manager if it's defined When in light box mode, switch to using 2d point handles for fiducials. Avoid seed widget and representation having different renderers on creation. Unify light box index calculation. Fix the annotations showing up in the 0th light box when the annotation should be in the -1th. Use floor rather than a cast to int to convert from floating point display coordinate to light box index to avoid rounding toward 0.0 in negative space. Take into account volume spacing, calculate the distance between the fiducial and the slice at this light box index using distance from the fiducial point and the slice plane. Reset the glyph type when changing in and out of lightbox mode. Issue #1690 --- .../vtkMRMLAnnotationDisplayableManager.cxx | 338 +++++++++++------- .../vtkMRMLAnnotationDisplayableManager.h | 12 + ...MLAnnotationFiducialDisplayableManager.cxx | 163 ++++++++- 3 files changed, 373 insertions(+), 140 deletions(-) diff --git a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx index 4470c6a5e..c789fa4f4 100644 --- a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx +++ b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx @@ -262,24 +262,11 @@ void vtkMRMLAnnotationDisplayableManager::UpdateFromMRML() // do we have a widget for it? if (this->GetWidget(annotationNode) == NULL) { - vtkDebugMacro("UpdateFromMRML: creating a widget for node " << annotationNode->GetID()); - vtkAbstractWidget *widget = this->CreateWidget(annotationNode); - if (widget) + vtkDebugMacro("UpdateFromMRML: adding node " << annotationNode->GetID()); + if (this->AddAnnotation(annotationNode)) { - // Add widget to the list - this->Helper->Widgets[annotationNode] = widget; - - // Add the node to the list. - this->Helper->AnnotationNodeList.push_back(annotationNode); - - // Refresh observers - this->SetAndObserveNode(annotationNode); - - // tear down widget creation - this->OnWidgetCreated(widget, annotationNode); - // update the new widget from the node - this->PropagateMRMLToWidget(annotationNode, widget); + this->PropagateMRMLToWidget(annotationNode, this->GetWidget(annotationNode)); } } } @@ -418,47 +405,12 @@ void vtkMRMLAnnotationDisplayableManager::OnMRMLSceneNodeAdded(vtkMRMLNode* node vtkDebugMacro("OnMRMLSceneNodeAddedEvent: node " << node->GetID()); - // Node added should not be already managed - vtkMRMLAnnotationDisplayableManagerHelper::AnnotationNodeListIt it = std::find( - this->Helper->AnnotationNodeList.begin(), - this->Helper->AnnotationNodeList.end(), - annotationNode); - if (it != this->Helper->AnnotationNodeList.end()) - { - vtkErrorMacro("OnMRMLSceneNodeAddedEvent: This node is already associated to the displayable manager!") - return; - } - - // There should not be a widget for the new node - if (this->Helper->GetWidget(annotationNode) != 0) + if (!this->AddAnnotation(annotationNode)) { - vtkErrorMacro("OnMRMLSceneNodeAddedEvent: A widget is already associated to this node!"); + vtkErrorMacro("OnMRMLSceneNodeAddedEvent: failed to add annotation node " << node->GetName()); return; } - //std::cout << "OnMRMLSceneNodeAddedEvent ThreeD -> CreateWidget" << std::endl; - - // Create the Widget and add it to the list. - vtkAbstractWidget* newWidget = this->CreateWidget(annotationNode); - if (!newWidget) - { - vtkErrorMacro("OnMRMLSceneNodeAddedEvent: Widget was not created!") - return; - } - this->Helper->Widgets[annotationNode] = newWidget; - - // Add the node to the list. - this->Helper->AnnotationNodeList.push_back(annotationNode); - - // Refresh observers - this->SetAndObserveNode(annotationNode); - - // TODO do we need this render call? - this->RequestRender(); - - // tear down widget creation - this->OnWidgetCreated(newWidget, annotationNode); - // Remove all placed seeds this->Helper->RemoveSeeds(); @@ -736,8 +688,10 @@ void vtkMRMLAnnotationDisplayableManager::OnMRMLSliceNodeModifiedEvent(vtkMRMLSl if (visibleOnSlice) { - // it's visible, just update the position - this->UpdatePosition(this->Helper->GetWidget(annotationNode), annotationNode); + // it's visible, but if just update the position, don't get updates + //necessary when switch into and out of lightbox + vtkDebugMacro("OnMRMLSliceNodeModifiedEvent: visible, propagate mrml to widget"); + this->PropagateMRMLToWidget(annotationNode, this->Helper->GetWidget(annotationNode)); } else @@ -890,15 +844,12 @@ bool vtkMRMLAnnotationDisplayableManager::IsWidgetDisplayable(vtkMRMLSliceNode* if (this->IsInLightboxMode()) { - /// BUG mantis issue 1690: if in lightbox mode, don't show fiducials or - /// rulers - if (!strcmp(this->m_Focus, "vtkMRMLAnnotationFiducialNode") || - !strcmp(this->m_Focus, "vtkMRMLAnnotationRulerNode")) + /// BUG mantis issue 1690: if in lightbox mode, don't show rulers + if (!strcmp(this->m_Focus, "vtkMRMLAnnotationRulerNode")) { return false; } } - int numberOfControlPoints = controlPointsNode->GetNumberOfControlPoints(); // the text node saves it's second control point in viewport coordinates, so @@ -926,7 +877,7 @@ bool vtkMRMLAnnotationDisplayableManager::IsWidgetDisplayable(vtkMRMLSliceNode* // get the corresponding lightbox index for this display coordinate and // check if it's in the range of the current number of light boxes being // displayed in the grid rows/columns. - int lightboxIndex = (int)(displayCoordinates[2]+0.5); + int lightboxIndex = this->GetLightboxIndex(controlPointsNode); int numberOfLightboxes = sliceNode->GetLayoutGridColumns() * sliceNode->GetLayoutGridRows(); //std::cout << "IsWidgetDisplayable: " << sliceNode->GetName() << ": lightbox mode, index = " << lightboxIndex << ", rows = " << sliceNode->GetLayoutGridRows() << ", cols = " << sliceNode->GetLayoutGridColumns() << ", number of light boxes = " << numberOfLightboxes << std::endl; if (lightboxIndex < 0 || @@ -937,84 +888,128 @@ bool vtkMRMLAnnotationDisplayableManager::IsWidgetDisplayable(vtkMRMLSliceNode* else { // get the right renderer index by checking the z coordinate - int rendererIndex = lightboxIndex; - - // get all renderers associated with this renderWindow - // when lightbox mode is enabled, there will be different renderers - // associated with the renderWindow of the sliceView (there may also - // be more renderers than light boxes) - vtkRendererCollection* rendererCollection = this->GetInteractor()->GetRenderWindow()->GetRenderers(); - - // check if the rendererIndex is valid for the current lightbox view - if (rendererIndex >= 0 && rendererIndex < rendererCollection->GetNumberOfItems()) + vtkRenderer* currentRenderer = this->GetRenderer(lightboxIndex); + + // now we get the widget.. + vtkAbstractWidget* widget = this->GetWidget(node); + + // TODO this code blocks the movement of the widget in lightbox mode + if (widget && + (widget->GetCurrentRenderer() != currentRenderer || + widget->GetRepresentation()->GetRenderer() != currentRenderer)) { - - vtkRenderer* currentRenderer = vtkRenderer::SafeDownCast(rendererCollection->GetItemAsObject(rendererIndex)); - - // now we get the widget.. - vtkAbstractWidget* widget = this->GetWidget(node); - - // TODO this code blocks the movement of the widget in lightbox mode - if (widget && - (widget->GetCurrentRenderer() != currentRenderer || - widget->GetRepresentation()->GetRenderer() != currentRenderer)) + vtkDebugMacro("IsWidgetDisplayable: updating renderer on widget and representation"); + // if the widget is on, need to turn it off to set the renderer + bool toggleOffOn = false; + if (widget->GetEnabled()) { - // if the widget is on, need to turn it off to set the renderer - bool toggleOffOn = false; - if (widget->GetEnabled()) - { - // turn it off.. - widget->Off(); - toggleOffOn = true; - } - // ..place it and its representation to the right renderer.. - widget->SetCurrentRenderer(currentRenderer); - widget->GetRepresentation()->SetRenderer(currentRenderer); - if (toggleOffOn) - { - // ..and turn it on again! - widget->On(); - } - // if it's a seed widget, go to complete interaction state - vtkSeedWidget *seedWidget = vtkSeedWidget::SafeDownCast(widget); - if (seedWidget) - { - vtkDebugMacro("SeedWidget: Complete interaction"); - seedWidget->CompleteInteraction(); - } + // turn it off.. + widget->Off(); + toggleOffOn = true; + } + // ..place it and its representation to the right renderer.. + + widget->SetCurrentRenderer(currentRenderer); + widget->GetRepresentation()->SetRenderer(currentRenderer); + + if (toggleOffOn) + { + // ..and turn it on again! + widget->On(); + } + // if it's a seed widget, go to complete interaction state + vtkSeedWidget *seedWidget = vtkSeedWidget::SafeDownCast(widget); + if (seedWidget) + { + vtkDebugMacro("SeedWidget: Complete interaction"); + seedWidget->CompleteInteraction(); + } - // we need to render again - if (currentRenderer) - { - currentRenderer->Render(); - } + // we need to render again + if (currentRenderer) + { + currentRenderer->Render(); } } - else - { - // it's out of range of the current collection of renderers - showWidget = false; - } + } // // End of Lightbox specific code // } - else + + // check if the annotation is close enough to the slice to be shown + if (showWidget) { - // the third coordinate of the displayCoordinates is the distance to the slice - float distanceToSlice = displayCoordinates[2]; - float maxDistance = 0.5 + (sliceNode->GetDimensions()[2] - 1); - if (distanceToSlice < -0.5 || distanceToSlice >= maxDistance) + if (this->IsInLightboxMode()) { - // if the distance to the slice is more than 0.5mm, we know that at least one coordinate of the widget is outside the current activeSlice - // hence, we do not want to show this widget - showWidget = false; - // we don't even need to continue parsing the controlpoints, because we know the widget will not be shown - break; + // get the volume's spacing to determine the distance between the slice + // location and the annotation + // default to spacing 1.0 in case can't get volume slice spacing from + // the logic as that will be a multiplicative no-op + double spacing = 1.0; + vtkMRMLSliceLogic *sliceLogic = NULL; + vtkMRMLApplicationLogic *mrmlAppLogic = this->GetMRMLApplicationLogic(); + if (mrmlAppLogic) + { + sliceLogic = mrmlAppLogic->GetSliceLogic(this->GetSliceNode()); + } + if (sliceLogic) + { + double *volumeSliceSpacing = sliceLogic->GetLowestVolumeSliceSpacing(); + if (volumeSliceSpacing != NULL) + { + vtkDebugMacro("Slice node " << this->GetSliceNode()->GetName() << ": volumeSliceSpacing = " << volumeSliceSpacing[0] << ", " << volumeSliceSpacing[1] << ", " << volumeSliceSpacing[2]); + spacing = volumeSliceSpacing[2]; + } + } + vtkDebugMacro("displayCoordinates: " << displayCoordinates[0] << "," << displayCoordinates[1] << "," << displayCoordinates[2] << "\n\tworld coords: " << transformedWorldCoordinates[0] << "," << transformedWorldCoordinates[1] << "," << transformedWorldCoordinates[2]); + // calculate the distance from the annotation in world space to the + // plane defined by the slice node normal and origin (using same + // convention as the vtkMRMLThreeDReformatDisplayableManager) + vtkMatrix4x4 *sliceToRAS = this->GetSliceNode()->GetSliceToRAS(); + double slicePlaneNormal[3], slicePlaneOrigin[3]; + slicePlaneNormal[0] = sliceToRAS->GetElement(0,2); + slicePlaneNormal[1] = sliceToRAS->GetElement(1,2); + slicePlaneNormal[2] = sliceToRAS->GetElement(2,2); + slicePlaneOrigin[0] = sliceToRAS->GetElement(0,3); + slicePlaneOrigin[1] = sliceToRAS->GetElement(1,3); + slicePlaneOrigin[2] = sliceToRAS->GetElement(2,3); + double distanceToPlane = slicePlaneNormal[0]*(transformedWorldCoordinates[0]-slicePlaneOrigin[0]) + + slicePlaneNormal[1]*(transformedWorldCoordinates[1]-slicePlaneOrigin[1]) + + slicePlaneNormal[2]*(transformedWorldCoordinates[2]-slicePlaneOrigin[2]); + // this gives the distance to light box plane 0, but have to offset by + // number of light box planes (as determined by the light box index) times the volume + // slice spacing + int lightboxIndex = this->GetLightboxIndex(controlPointsNode); + double lightboxOffset = lightboxIndex * spacing; + double distanceToSlice = distanceToPlane - lightboxOffset; + double maxDistance = 0.5; + vtkDebugMacro("\n\tdistance to plane = " << distanceToPlane << "\n\tlightboxIndex = " << lightboxIndex << "\n\tlightboxOffset = " << lightboxOffset << "\n\tdistance to slice = " << distanceToSlice); + // check that it's within 0.5mm + if (distanceToSlice < -0.5 || distanceToSlice >= maxDistance) + { + vtkDebugMacro("Distance to slice is greater than max distance, not showing the widget"); + showWidget = false; + break; + } + } + else + { + // the third coordinate of the displayCoordinates is the distance to the slice + float distanceToSlice = displayCoordinates[2]; + float maxDistance = 0.5 + (sliceNode->GetDimensions()[2] - 1); + vtkDebugMacro("Slice node " << this->GetSliceNode()->GetName() << ": distance to slice = " << distanceToSlice << ", maxDistance = " << maxDistance << "\n\tslice node dimenions[2] = " << sliceNode->GetDimensions()[2]); + if (distanceToSlice < -0.5 || distanceToSlice >= maxDistance) + { + // if the distance to the slice is more than 0.5mm, we know that at least one coordinate of the widget is outside the current activeSlice + // hence, we do not want to show this widget + showWidget = false; + // we don't even need to continue parsing the controlpoints, because we know the widget will not be shown + break; + } } } - // ----------------------------------------- // special cases when the slices get panned: @@ -1623,3 +1618,96 @@ bool vtkMRMLAnnotationDisplayableManager::IsInLightboxMode() return flag; } + + +//--------------------------------------------------------------------------- +int vtkMRMLAnnotationDisplayableManager::GetLightboxIndex(vtkMRMLAnnotationNode *node, int controlPointIndex) +{ + int index = -1; + + if (!node) + { + return index; + } + if (!this->IsInLightboxMode()) + { + return index; + } + + // down cast the node as a controlpoints node to get the coordinates + vtkMRMLAnnotationControlPointsNode * controlPointsNode = vtkMRMLAnnotationControlPointsNode::SafeDownCast(node); + + if (!controlPointsNode) + { + vtkErrorMacro("GetLightboxIndex: Could not get the controlpoints node.") + return index; + } + + if (controlPointIndex < 0 || + controlPointIndex >= controlPointsNode->GetNumberOfControlPoints()) + { + return index; + } + + double transformedWorldCoordinates[4]; + controlPointsNode->GetControlPointWorldCoordinates(controlPointIndex, transformedWorldCoordinates); + double displayCoordinates[4]; + this->GetWorldToDisplayCoordinates(transformedWorldCoordinates,displayCoordinates); + + index = (int)(floor(displayCoordinates[2]+0.5)); + + + return index; +} + +//--------------------------------------------------------------------------- +bool vtkMRMLAnnotationDisplayableManager::AddAnnotation(vtkMRMLAnnotationNode *annotationNode) +{ + if (!annotationNode || !this->GetMRMLScene()) + { + return false; + } + + // Node added should not be already managed + vtkMRMLAnnotationDisplayableManagerHelper::AnnotationNodeListIt it = std::find( + this->Helper->AnnotationNodeList.begin(), + this->Helper->AnnotationNodeList.end(), + annotationNode); + if (it != this->Helper->AnnotationNodeList.end()) + { + vtkErrorMacro("AddAnnotation: This node is already associated to the displayable manager!"); + return false; + } + + // There should not be a widget for the new node + if (this->Helper->GetWidget(annotationNode) != 0) + { + vtkErrorMacro("AddAnnotation: A widget is already associated to this node!"); + return false; + } + + vtkDebugMacro("AddAnnotation: ThreeD -> CreateWidget"); + + // Create the Widget and add it to the list. + vtkAbstractWidget* newWidget = this->CreateWidget(annotationNode); + if (!newWidget) + { + vtkErrorMacro("AddAnnotation: Widget was not created!") + return false; + } + this->Helper->Widgets[annotationNode] = newWidget; + + // Add the node to the list. + this->Helper->AnnotationNodeList.push_back(annotationNode); + + // Refresh observers + this->SetAndObserveNode(annotationNode); + + // TODO do we need this render call? + this->RequestRender(); + + // tear down widget creation + this->OnWidgetCreated(newWidget, annotationNode); + + return true; +} diff --git a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.h b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.h index 4b5a89f8c..8e2cb2f18 100644 --- a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.h +++ b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.h @@ -88,6 +88,18 @@ public: /// grid columns or rows > 1 bool IsInLightboxMode(); + /// Gets the world coordinate of the annotation node, transforms it to + /// display coordinates. + /// Defaults to returning the 0th control point's light box index. Returns + /// -1 if not in lightbox mode. + int GetLightboxIndex(vtkMRMLAnnotationNode *node, int controlPointIndex = 0); + + /// Set up data structures for an annotation node. Returns false on failure + /// or if it's already set up. Can be called to reinitialise a node's widgets + /// after calling RemoveWidgetAndNode on the Helper + /// \sa vtkMRMLAnnotationDisplayableManagerHelper::RemoveWidgetAndNode() + bool AddAnnotation(vtkMRMLAnnotationNode *node); + protected: vtkMRMLAnnotationDisplayableManager(); diff --git a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationFiducialDisplayableManager.cxx b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationFiducialDisplayableManager.cxx index 245959bad..595cd7336 100644 --- a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationFiducialDisplayableManager.cxx +++ b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationFiducialDisplayableManager.cxx @@ -104,7 +104,11 @@ public: // only if we had to restrict the coordinates aka. if the coordinates changed, we update the positions if (this->m_DisplayableManager->GetDisplayCoordinatesChanged(displayCoordinates1,restrictedDisplayCoordinates1)) { - representation->SetSeedDisplayPosition(0,restrictedDisplayCoordinates1); + if (representation->GetRenderer() && + representation->GetRenderer()->GetActiveCamera()) + { + representation->SetSeedDisplayPosition(0,restrictedDisplayCoordinates1); + } } } @@ -180,18 +184,35 @@ vtkAbstractWidget * vtkMRMLAnnotationFiducialDisplayableManager::CreateWidget(vt std::cout<<"No DisplayNode!"<::iterator iter = this->NodeGlyphTypes.find(displayNode); + if (iter != this->NodeGlyphTypes.end()) + { + vtkDebugMacro("CreateWidget: found a glyph type already defined for this node: " << iter->second); + this->NodeGlyphTypes[displayNode] = vtkMRMLAnnotationPointDisplayNode::GlyphMin - 1; + } vtkNew rep; - vtkNew handle; - - // default to a starburst glyph, update in propagate mrml to widget - vtkNew glyphSource; - glyphSource->SetGlyphType(vtkMRMLAnnotationPointDisplayNode::StarBurst2D); - glyphSource->Update(); - glyphSource->SetScale(1.0); - handle->SetHandle(glyphSource->GetOutput()); + if (!this->IsInLightboxMode()) + { + vtkDebugMacro("CreateWidget: not in light box mode, making a 3d handle"); + vtkNew handle; + // default to a sphere glyph, update in propagate mrml to widget + vtkNew glyphSource; + glyphSource->SetGlyphType(vtkMRMLAnnotationPointDisplayNode::Sphere3D); + glyphSource->Update(); + glyphSource->SetScale(1.0); + handle->SetHandle(glyphSource->GetOutput()); + rep->SetHandleRepresentation(handle.GetPointer()); + } + else + { + vtkDebugMacro("CreateWidget: in light box mode, making a 2d handle"); + vtkNew handle; + rep->SetHandleRepresentation(handle.GetPointer()); + } - rep->SetHandleRepresentation(handle.GetPointer()); + //seed widget @@ -201,7 +222,18 @@ vtkAbstractWidget * vtkMRMLAnnotationFiducialDisplayableManager::CreateWidget(vt seedWidget->SetRepresentation(rep.GetPointer()); seedWidget->SetInteractor(this->GetInteractor()); - seedWidget->SetCurrentRenderer(this->GetRenderer()); + // set the renderer on the widget and representation + if (!this->IsInLightboxMode()) + { + seedWidget->SetCurrentRenderer(this->GetRenderer()); + seedWidget->GetRepresentation()->SetRenderer(this->GetRenderer()); + } + else + { + int lightboxIndex = this->GetLightboxIndex(fiducialNode); + seedWidget->SetCurrentRenderer(this->GetRenderer(lightboxIndex)); + seedWidget->GetRepresentation()->SetRenderer(this->GetRenderer(lightboxIndex)); + } //seedWidget->ProcessEventsOff(); @@ -297,7 +329,7 @@ void vtkMRMLAnnotationFiducialDisplayableManager::PropagateMRMLToWidget(vtkMRMLA vtkErrorMacro("PropagateMRMLToWidget: Could not get seed widget!") return; } - + // cast to the specific mrml node vtkMRMLAnnotationFiducialNode* fiducialNode = vtkMRMLAnnotationFiducialNode::SafeDownCast(node); @@ -312,7 +344,11 @@ void vtkMRMLAnnotationFiducialDisplayableManager::PropagateMRMLToWidget(vtkMRMLA // now get the widget properties (coordinates, measurement etc.) and if the mrml node has changed, propagate the changes vtkSeedRepresentation * seedRepresentation = vtkSeedRepresentation::SafeDownCast(seedWidget->GetRepresentation()); - + if (!seedRepresentation) + { + vtkErrorMacro("PropagateMRMLToWidget: Could not get seed representation from widget!") + return; + } vtkMRMLAnnotationPointDisplayNode *displayNode = fiducialNode->GetAnnotationPointDisplayNode(); @@ -322,6 +358,54 @@ void vtkMRMLAnnotationFiducialDisplayableManager::PropagateMRMLToWidget(vtkMRMLA } vtkOrientedPolygonalHandleRepresentation3D *handleRep = vtkOrientedPolygonalHandleRepresentation3D::SafeDownCast(seedRepresentation->GetHandleRepresentation(0)); + // might be in lightbox mode where using a 2d point handle + vtkPointHandleRepresentation2D *pointHandleRep = vtkPointHandleRepresentation2D::SafeDownCast(seedRepresentation->GetHandleRepresentation(0)); + // double check that if switch in and out of light box mode, the handle rep + // is updated + bool updateHandleType = false; + if (this->IsInLightboxMode()) + { + if (handleRep) + { + vtkDebugMacro("PropagateMRMLToWidget: have a 3d handle representation in 2d light box, resetting it."); + updateHandleType = true; + } + } + else + { + if (pointHandleRep) + { + vtkDebugMacro("PropagateMRMLToWidget: Not in light box, but have a point handle."); + updateHandleType = true; + } + } + if (updateHandleType) + { + vtkDebugMacro("PropagateMRMLToWidget: removing widget..."); + // clean it out + this->Helper->RemoveWidgetAndNode(node); + // recreate it + vtkMRMLAnnotationNode *annotationNode = vtkMRMLAnnotationNode::SafeDownCast(node); + if (annotationNode) + { + this->AddAnnotation(annotationNode); + } + // did it come back in here + vtkDebugMacro("PropagateMRMLToWidget: did it end up calling this method already?"); + // probably not, so call and return + vtkAbstractWidget * widget = this->Helper->GetWidget(annotationNode); + if (widget) + { + this->PropagateMRMLToWidget(annotationNode, widget); + } + else + { + vtkWarningMacro("PropagateMRMLToWidget: failed to add a new widget for node " << node->GetName()); + return; + } + vtkDebugMacro("PropagateMRMLToWidget: NOW returning after calling self from self"); + return; + } if (handleRep) { if (displayNode) @@ -464,7 +548,50 @@ void vtkMRMLAnnotationFiducialDisplayableManager::PropagateMRMLToWidget(vtkMRMLA handleRep->LabelVisibilityOff(); } }//if (handleRep) - + else if (pointHandleRep) + { + if (displayNode) + { + // glyph type + /* + std::map::iterator iter = this->NodeGlyphTypes.find(displayNode); + if (iter == this->NodeGlyphTypes.end() || iter->second != displayNode->GetGlyphType()) + { + // map the 3d sphere to a filled circle, the 3d diamond to a filled + // diamond + vtkNew glyphSource; + if (displayNode->GetGlyphType() == vtkMRMLAnnotationPointDisplayNode::Sphere3D) + { + glyphSource->SetGlyphType(vtkMRMLAnnotationPointDisplayNode::Circle2D); + } + else if (displayNode->GetGlyphType() == vtkMRMLAnnotationPointDisplayNode::Diamond3D) + { + glyphSource->SetGlyphType(vtkMRMLAnnotationPointDisplayNode::Diamond2D); + } + else + { + glyphSource->SetGlyphType(displayNode->GetGlyphType()); + } + glyphSource->Update(); + glyphSource->SetScale(1.0); + std::cout << "PropagateMRMLToWidget: " << this->GetSliceNode()->GetName() << ": setting point handle rep cursor shape " << glyphSource->GetOutput() << std::endl; + pointHandleRep->SetCursorShape(glyphSource->GetOutput()); + this->NodeGlyphTypes[displayNode] = displayNode->GetGlyphType(); + } + */ + // set the color + if (fiducialNode->GetSelected()) + { + // use the selected color + pointHandleRep->GetProperty()->SetColor(displayNode->GetSelectedColor()); + } + else + { + // use the unselected color + pointHandleRep->GetProperty()->SetColor(displayNode->GetColor()); + } + } + } // now update the position this->UpdatePosition(widget, node); @@ -762,7 +889,13 @@ void vtkMRMLAnnotationFiducialDisplayableManager::UpdatePosition(vtkAbstractWidg { // only update when really changed vtkDebugMacro("UpdatePosition: " << this->GetSliceNode()->GetName() << ": display coordinates changed:\n\tseed display = " << displayCoordinatesBuffer1[0] << ", " << displayCoordinatesBuffer1[1] << "\n\tfid display = " << displayCoordinates1[0] << ", " << displayCoordinates1[1] ); - seedRepresentation->SetSeedDisplayPosition(0,displayCoordinates1); + // make sure the representation has a renderer and an active camera to + // avoid a crash in vtkRenderer::ViewToWorld + if (seedRepresentation->GetRenderer() && + seedRepresentation->GetRenderer()->GetActiveCamera()) + { + seedRepresentation->SetSeedDisplayPosition(0,displayCoordinates1); + } positionChanged = true; } else -- GitLab