diff --git a/Modules/Loadable/Annotations/MRML/vtkMRMLAnnotationLineDisplayNode.cxx b/Modules/Loadable/Annotations/MRML/vtkMRMLAnnotationLineDisplayNode.cxx index 88b8d3e2252d085c3025824b14eecaf86cbaa9b5..5fe1d2b5ebc746a9528a086604b479592517ce45 100644 --- a/Modules/Loadable/Annotations/MRML/vtkMRMLAnnotationLineDisplayNode.cxx +++ b/Modules/Loadable/Annotations/MRML/vtkMRMLAnnotationLineDisplayNode.cxx @@ -19,6 +19,15 @@ vtkMRMLAnnotationLineDisplayNode::vtkMRMLAnnotationLineDisplayNode() this->LabelVisibility = 1; this->TickSpacing = 10.0; this->MaxTicks = 99; + this->SliceProjection = (vtkMRMLAnnotationLineDisplayNode::ProjectionOff | + vtkMRMLAnnotationLineDisplayNode::ProjectionDotted | + vtkMRMLAnnotationLineDisplayNode::ProjectionColoredWhenParallel); + + // Default line color: white + this->SliceProjectionLineColor[0] = 1.0; + this->SliceProjectionLineColor[1] = 1.0; + this->SliceProjectionLineColor[2] = 1.0; + this->SliceProjectionLineColor[3] = 1.0; /// bug 2375: don't show the slice intersection until it's correct this->SliceIntersectionVisibility = 0; @@ -37,6 +46,14 @@ void vtkMRMLAnnotationLineDisplayNode::WriteXML(ostream& of, int nIndent) of << " labelVisibility=\"" << (this->LabelVisibility ? "true" : "false") << "\""; of << " tickSpacing=\"" << this->TickSpacing << "\""; of << " maxTicks=\"" << this->MaxTicks << "\""; + of << " sliceProjection=\"" << this->SliceProjection << "\""; + + if (this->SliceProjectionLineColor) + { + of << indent << " sliceProjectionLineColor=\"" << this->SliceProjectionLineColor[0] << " " + << this->SliceProjectionLineColor[1] << " " + << this->SliceProjectionLineColor[2] << "\""; + } } //---------------------------------------------------------------------------- @@ -88,6 +105,20 @@ void vtkMRMLAnnotationLineDisplayNode::ReadXMLAttributes(const char** atts) ss << attValue; ss >> this->MaxTicks; } + else if (!strcmp(attName, "sliceProjection")) + { + std::stringstream ss; + ss << attValue; + ss >> this->SliceProjection; + } + else if (!strcmp(attName, "sliceProjectionLineColor")) + { + std::stringstream ss; + ss << attValue; + ss >> this->SliceProjectionLineColor[0]; + ss >> this->SliceProjectionLineColor[1]; + ss >> this->SliceProjectionLineColor[2]; + } } this->EndModify(disabledModify); } @@ -106,6 +137,8 @@ void vtkMRMLAnnotationLineDisplayNode::Copy(vtkMRMLNode *anode) this->SetLabelVisibility(node->LabelVisibility); this->SetTickSpacing(node->TickSpacing); this->SetMaxTicks(node->MaxTicks); + this->SetSliceProjection(node->SliceProjection); + this->SetSliceProjectionLineColor(node->GetSliceProjectionLineColor()); this->EndModify(disabledModify); } @@ -119,6 +152,12 @@ void vtkMRMLAnnotationLineDisplayNode::PrintSelf(ostream& os, vtkIndent indent) os << indent << "Label Visibility : " << (this->LabelVisibility ? "true" : "false") << "\n"; os << indent << "Tick Spacing : " << this->TickSpacing << "\n"; os << indent << "Max Ticks : " << this->MaxTicks << "\n"; + os << indent << "Slice Projection : " << this->SliceProjection << "\n"; + os << indent << "Slice Projection Line Color : (" + << this->SliceProjectionLineColor[0] << "," + << this->SliceProjectionLineColor[1] << "," + << this->SliceProjectionLineColor[2] << "," + << this->SliceProjectionLineColor[3] << ")" << "\n"; } //--------------------------------------------------------------------------- diff --git a/Modules/Loadable/Annotations/MRML/vtkMRMLAnnotationLineDisplayNode.h b/Modules/Loadable/Annotations/MRML/vtkMRMLAnnotationLineDisplayNode.h index b5e5bbf720e1481b78e90a142f434d3664a791dc..bda80571f8d4777352cae0d51ed0431d56fab227 100644 --- a/Modules/Loadable/Annotations/MRML/vtkMRMLAnnotationLineDisplayNode.h +++ b/Modules/Loadable/Annotations/MRML/vtkMRMLAnnotationLineDisplayNode.h @@ -76,6 +76,52 @@ class VTK_SLICER_ANNOTATIONS_MODULE_MRML_EXPORT vtkMRMLAnnotationLineDisplayNod vtkSetMacro(MaxTicks, int); vtkGetMacro(MaxTicks, int); + /// Set SliceProjection flag + /// ProjectionOff, Dotted and ColoredWhenParallel by default. + /// ProjectionOff : No projection + /// ProjectionOn : Projection with plain line + /// ProjectionDotted : Projection with dotted line when + /// not in the plane + /// ProjectionColoredWhenParallel : Color line if parallel + /// to slice plane + /// \sa SliceIntersectionVisibilty, ProjectedLineColor + vtkGetMacro(SliceProjection, int); + vtkSetMacro(SliceProjection, int); + + /// Set SliceProjection to On + inline void SetSliceProjectionOn(); + + /// Set SliceProjection to Off + inline void SetSliceProjectionOff(); + + /// Set SliceProjection to Dotted + inline void SetSliceProjectionDottedOn(); + + /// Set SliceProjection to Plain + inline void SetSliceProjectionDottedOff(); + + /// Set line colored when parallel to slice plane + inline void SetSliceProjectionColoredWhenParallelOn(); + + /// Set line color unchanged when parallel to slice plane + inline void SetSliceProjectionColoredWhenParallelOff(); + + /// \enumthis->SliceProjection ProjectionFlag + enum ProjectionFlag + { + ProjectionOff = 0x00, + ProjectionOn = 0x01, + ProjectionDotted = 0x02, + ProjectionColoredWhenParallel = 0x04, + //ProjectionSparseLineStipplePattern = 0x08 + }; + + /// Set color of the projected line on the 2D viewers + /// when parallel to the slice plane + /// White (1.0, 1.0, 1.0, 1.0) by default. + vtkGetVector4Macro(SliceProjectionLineColor, double); + vtkSetVector4Macro(SliceProjectionLineColor, double); + /// Create a backup of this node and attach it. void CreateBackup(); /// Restore an attached backup of this node. @@ -92,6 +138,57 @@ protected: int LabelVisibility; double TickSpacing; int MaxTicks; + int SliceProjection; + double SliceProjectionLineColor[4]; }; +//---------------------------------------------------------------------------- +void vtkMRMLAnnotationLineDisplayNode +::SetSliceProjectionOn() +{ + this->SetSliceProjection( this->GetSliceProjection() | + vtkMRMLAnnotationLineDisplayNode::ProjectionOn); +} + +//---------------------------------------------------------------------------- +void vtkMRMLAnnotationLineDisplayNode +::SetSliceProjectionOff() +{ + this->SetSliceProjection( this->GetSliceProjection() & + ~vtkMRMLAnnotationLineDisplayNode::ProjectionOn); +} + +//---------------------------------------------------------------------------- +void vtkMRMLAnnotationLineDisplayNode +::SetSliceProjectionDottedOn() +{ + this->SetSliceProjection( this->GetSliceProjection() | + vtkMRMLAnnotationLineDisplayNode::ProjectionDotted); +} + +//---------------------------------------------------------------------------- +void vtkMRMLAnnotationLineDisplayNode +::SetSliceProjectionDottedOff() +{ + this->SetSliceProjection( this->GetSliceProjection() & + ~vtkMRMLAnnotationLineDisplayNode::ProjectionDotted); +} + +//---------------------------------------------------------------------------- +void vtkMRMLAnnotationLineDisplayNode +::SetSliceProjectionColoredWhenParallelOn() +{ + this->SetSliceProjection( this->GetSliceProjection() | + vtkMRMLAnnotationLineDisplayNode::ProjectionColoredWhenParallel); +} + +//---------------------------------------------------------------------------- +void vtkMRMLAnnotationLineDisplayNode +::SetSliceProjectionColoredWhenParallelOff() +{ + this->SetSliceProjection( this->GetSliceProjection() & + ~vtkMRMLAnnotationLineDisplayNode::ProjectionColoredWhenParallel); +} + + #endif diff --git a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx index e0951b267ecb8779a45455648b9b3153fb90df4d..f90d3c820c739facc12044ae63ed5ca9c12c7282 100644 --- a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx +++ b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManager.cxx @@ -33,11 +33,14 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -686,6 +689,18 @@ void vtkMRMLAnnotationDisplayableManager::OnMRMLSliceNodeModifiedEvent(vtkMRMLSl // 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"); + + // If visible, turn off projection + vtkMRMLAnnotationRulerNode* rulerNode = vtkMRMLAnnotationRulerNode::SafeDownCast(annotationNode); + if (rulerNode) + { + vtkLineWidget2* line = vtkLineWidget2::SafeDownCast(this->Helper->GetProjectionWidget(rulerNode)); + if (line) + { + line->Off(); + } + } + this->PropagateMRMLToWidget(annotationNode, this->Helper->GetWidget(annotationNode)); } @@ -697,7 +712,7 @@ void vtkMRMLAnnotationDisplayableManager::OnMRMLSliceNodeModifiedEvent(vtkMRMLSl vtkMRMLAnnotationRulerNode* rulerNode = vtkMRMLAnnotationRulerNode::SafeDownCast(annotationNode); if (rulerNode && - (rulerNode->GetAnnotationLineDisplayNode() && rulerNode->GetAnnotationLineDisplayNode()->GetSliceIntersectionVisibility())) + (rulerNode->GetAnnotationLineDisplayNode())) { double transformedP1[4]; @@ -724,84 +739,179 @@ void vtkMRMLAnnotationDisplayableManager::OnMRMLSliceNodeModifiedEvent(vtkMRMLSl // compute intersection with slice plane // if !=0: mark the intersection - //double this->GetSliceNode()->GetSliceOffset() = p1[2] + (p2[2]-p1[2])*t; - // t = (this->GetSliceNode()->GetSliceOffset() - p1[2]) / (p2[2]-p1[2]) - double t = (this->GetSliceNode()->GetSliceOffset()-displayP1[2]) / (displayP2[2]-displayP1[2]); + if(rulerNode->GetAnnotationLineDisplayNode()->GetSliceIntersectionVisibility()) + { + //double this->GetSliceNode()->GetSliceOffset() = p1[2] + (p2[2]-p1[2])*t; + // t = (this->GetSliceNode()->GetSliceOffset() - p1[2]) / (p2[2]-p1[2]) + double t = (this->GetSliceNode()->GetSliceOffset()-displayP1[2]) / (displayP2[2]-displayP1[2]); - // p2-p1 - double P2minusP1[3]; - vtkMath::Subtract(displayP2,displayP1,P2minusP1); + // p2-p1 + double P2minusP1[3]; + vtkMath::Subtract(displayP2,displayP1,P2minusP1); - // (p2-p1)*t - vtkMath::MultiplyScalar(P2minusP1,t); + // (p2-p1)*t + vtkMath::MultiplyScalar(P2minusP1,t); - // p1 + ((p2-p1)*t) - double P1plusP2minusP1[3]; - vtkMath::Add(displayP1,P2minusP1,P1plusP2minusP1); + // p1 + ((p2-p1)*t) + double P1plusP2minusP1[3]; + vtkMath::Add(displayP1,P2minusP1,P1plusP2minusP1); - // Since we have the position of the intersection now, - // we want to show it using a marker inside the sliceViews. - // - // We query the list if we already have a marker for this special widget. - // If not, we create a new marker. - // In any case, we will move the marker (either the newly created or the old). + // Since we have the position of the intersection now, + // we want to show it using a marker inside the sliceViews. + // + // We query the list if we already have a marker for this special widget. + // If not, we create a new marker. + // In any case, we will move the marker (either the newly created or the old). - vtkSeedWidget* marker = vtkSeedWidget::SafeDownCast(this->Helper->GetIntersectionWidget(rulerNode)); + vtkSeedWidget* marker = vtkSeedWidget::SafeDownCast(this->Helper->GetIntersectionWidget(rulerNode)); - if (!marker) - { - // we create a new marker. + if (!marker) + { + // we create a new marker. - vtkNew handle; - handle->GetProperty()->SetColor(0,1,0); - handle->SetHandleSize(3); + vtkNew handle; + handle->GetProperty()->SetColor(0,1,0); + handle->SetHandleSize(3); - vtkNew rep; - rep->SetHandleRepresentation(handle.GetPointer()); + vtkNew rep; + rep->SetHandleRepresentation(handle.GetPointer()); - marker = vtkSeedWidget::New(); + marker = vtkSeedWidget::New(); - marker->CreateDefaultRepresentation(); + marker->CreateDefaultRepresentation(); - marker->SetRepresentation(rep.GetPointer()); + marker->SetRepresentation(rep.GetPointer()); - marker->SetInteractor(this->GetInteractor()); - marker->SetCurrentRenderer(this->GetRenderer()); + marker->SetInteractor(this->GetInteractor()); + marker->SetCurrentRenderer(this->GetRenderer()); - marker->ProcessEventsOff(); + marker->ProcessEventsOff(); - marker->On(); - marker->CompleteInteraction(); + marker->On(); + marker->CompleteInteraction(); + + // we save the marker in our WidgetIntersection list associated to this node + this->Helper->WidgetIntersections[rulerNode] = marker; + } - // we save the marker in our WidgetIntersection list associated to this node - this->Helper->WidgetIntersections[rulerNode] = marker; + // remove all old markers associated with this node + marker->DeleteSeed(0); + // the third component of the displayCoordinates is the distance to the slice + // now make sure that they have different signs + if ((displayP1[2] > 0 && displayP2[2] > 0) || + (displayP1[2] < 0 && displayP2[2] < 0)) + { + // jump out because they do not have different signs + ++it; + continue; + } + + // .. and create a new one at the intersection location + vtkSmartPointer newhandle = marker->CreateNewHandle(); + vtkHandleRepresentation::SafeDownCast(newhandle->GetRepresentation())->SetDisplayPosition(P1plusP2minusP1); + marker->On(); + marker->CompleteInteraction(); } - // remove all old markers associated with this node - marker->DeleteSeed(0); + // Display projection on 2D viewers + vtkLineWidget2* line = vtkLineWidget2::SafeDownCast(this->Helper->GetProjectionWidget(rulerNode)); + vtkMRMLAnnotationLineDisplayNode* lineDisplayNode = rulerNode->GetAnnotationLineDisplayNode(); - // the third component of the displayCoordinates is the distance to the slice - // now make sure that they have different signs - if ((displayP1[2] > 0 && displayP2[2] > 0) || - (displayP1[2] < 0 && displayP2[2] < 0)) + if (lineDisplayNode) { - // jump out because they do not have different signs - ++it; - continue; - } + if (lineDisplayNode->GetSliceProjection() & lineDisplayNode->ProjectionOn) + { + if (!line) + { + vtkNew handle; + handle->GetProperty()->SetOpacity(0.0); + handle->GetSelectedProperty()->SetOpacity(0.0); + handle->SetHandleSize(0); + + vtkNew rep; + rep->SetHandleRepresentation(handle.GetPointer()); + rep->GetPoint1Representation()->GetProperty()->SetOpacity(0.0); + rep->GetPoint1Representation()->GetSelectedProperty()->SetOpacity(0.0); + rep->GetPoint2Representation()->GetProperty()->SetOpacity(0.0); + rep->GetPoint2Representation()->GetSelectedProperty()->SetOpacity(0.0); + rep->GetLineHandleRepresentation()->GetProperty()->SetOpacity(0.0); + rep->GetLineHandleRepresentation()->GetSelectedProperty()->SetOpacity(0.0); + + line = vtkLineWidget2::New(); + line->CreateDefaultRepresentation(); + line->SetRepresentation(rep.GetPointer()); + line->SetInteractor(this->GetInteractor()); + line->SetCurrentRenderer(this->GetRenderer()); + line->ProcessEventsOff(); + this->Helper->WidgetProjections[rulerNode] = line; + } - // .. and create a new one at the intersection location - vtkSmartPointer newhandle = marker->CreateNewHandle(); - vtkHandleRepresentation::SafeDownCast(newhandle->GetRepresentation())->SetDisplayPosition(P1plusP2minusP1); - marker->On(); - marker->CompleteInteraction(); + vtkLineRepresentation* lineRep = vtkLineRepresentation::SafeDownCast(line->GetRepresentation()); + if (lineRep) + { + line->Off(); - //std::cout << this->GetSliceNode()->GetName() << ": " << P1plusP2minusP1[0] << "," << P1plusP2minusP1[1] << "," << P1plusP2minusP1[2] << std::endl; + double colorLine[4]; + lineDisplayNode->GetSliceProjectionLineColor(colorLine); - } + short linePattern = 0xFFFF; + + if (lineDisplayNode->GetSliceProjection() & lineDisplayNode->ProjectionDotted) + { + linePattern = 0xFF00; + } + // TODO: Modifiy pattern to have spaced dot line when further from plane, smaller space when closer + // 0x0001 + (0x0001 + 1) = 0x0003 (0000 0000 0000 0011) + // 0x0003 + (0x0003 + 1) = 0x0007 (0000 0000 0000 0111) + // 0x0007 + (0x0007 + 1) = 0x000F (0000 0000 0000 1111) + // etc... + + if (lineDisplayNode->GetSliceProjection() & lineDisplayNode->ProjectionColoredWhenParallel) + { + vtkMatrix4x4 *sliceToRAS = this->GetSliceNode()->GetSliceToRAS(); + double slicePlaneNormal[3], rulerVector[3]; + slicePlaneNormal[0] = sliceToRAS->GetElement(0,2); + slicePlaneNormal[1] = sliceToRAS->GetElement(1,2); + slicePlaneNormal[2] = sliceToRAS->GetElement(2,2); + rulerVector[0] = transformedP2[0] - transformedP1[0]; + rulerVector[1] = transformedP2[1] - transformedP1[1]; + rulerVector[2] = transformedP2[2] - transformedP1[2]; + + vtkMath::Normalize(slicePlaneNormal); + vtkMath::Normalize(rulerVector); + + static const double LineInPlaneError = 0.005; + if (fabs(vtkMath::Dot(slicePlaneNormal, rulerVector)) < LineInPlaneError) + { + sliceNode->GetLayoutColor(colorLine[0], colorLine[1], colorLine[2]); + linePattern = 0xFFFF; + } + } + + // Should be reset when widget is turned off, otherwise handle is rendered + lineRep->GetPoint1Representation()->GetSelectedProperty()->SetOpacity(0.0); + lineRep->GetPoint2Representation()->GetSelectedProperty()->SetOpacity(0.0); + + lineRep->GetLineProperty()->SetColor(colorLine[0],colorLine[1], colorLine[2]); + lineRep->GetLineProperty()->SetOpacity(colorLine[3]); + lineRep->GetLineProperty()->SetLineStipplePattern(linePattern); + line->On(); + lineRep->SetPoint1DisplayPosition(displayP1); + lineRep->SetPoint2DisplayPosition(displayP2); + } + } + else + { + if (line) + { + line->Off(); + } + } + } + } } ++it; diff --git a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManagerHelper.cxx b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManagerHelper.cxx index f101a9be530c0558449fc3bb0b209af55b2694f9..b4baccffdce19b226389333570ea2d5e0e86d168 100644 --- a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManagerHelper.cxx +++ b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManagerHelper.cxx @@ -247,6 +247,25 @@ vtkAbstractWidget * vtkMRMLAnnotationDisplayableManagerHelper::GetIntersectionWi return it->second; } +//--------------------------------------------------------------------------- +vtkAbstractWidget * vtkMRMLAnnotationDisplayableManagerHelper::GetProjectionWidget( + vtkMRMLAnnotationNode * node) +{ + if (!node) + { + return 0; + } + + // Make sure the map contains a vtkWidget associated with this node + WidgetProjectionsIt it = this->WidgetProjections.find(node); + if (it == this->WidgetProjections.end()) + { + return 0; + } + + return it->second; +} + //--------------------------------------------------------------------------- void vtkMRMLAnnotationDisplayableManagerHelper::RemoveAllWidgetsAndNodes() { @@ -270,6 +289,16 @@ void vtkMRMLAnnotationDisplayableManagerHelper::RemoveAllWidgetsAndNodes() } this->WidgetIntersections.clear(); + WidgetProjectionsIt projIt; + for (projIt = this->WidgetProjections.begin(); + projIt != this->WidgetProjections.end(); + ++projIt) + { + projIt->second->Off(); + projIt->second->Delete(); + } + this->WidgetProjections.clear(); + this->AnnotationNodeList.clear(); } diff --git a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManagerHelper.h b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManagerHelper.h index 3ae40feddd229c0d6b0f2d68dadc716fba2eab28..d44705d7ca10a5a5c64d4d533c3433d5e2b5dc39 100644 --- a/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManagerHelper.h +++ b/Modules/Loadable/Annotations/MRMLDisplayableManager/vtkMRMLAnnotationDisplayableManagerHelper.h @@ -27,6 +27,7 @@ class vtkMRMLInteractionNode; // VTK includes #include +#include #include #include @@ -62,6 +63,8 @@ public: vtkAbstractWidget * GetWidget(vtkMRMLAnnotationNode * node); /// ...an its associated vtkAbstractWidget* for Slice intersection representation vtkAbstractWidget * GetIntersectionWidget(vtkMRMLAnnotationNode * node); + /// ...an its associated vtkAbstractWidget* for Slice intersection representation + vtkAbstractWidget * GetProjectionWidget(vtkMRMLAnnotationNode * node); /// Remove all widgets, intersection widgets, nodes void RemoveAllWidgetsAndNodes(); /// Remove a node, its widget and its intersection widget @@ -97,6 +100,13 @@ public: /// .. and its associated convenient typedef typedef std::map::iterator WidgetIntersectionsIt; + + /// Map of vtkWidgets to reflect the Slice projection indexed using associated node ID + std::map WidgetProjections; + + /// .. and its associated convenient typedef + typedef std::map::iterator WidgetProjectionsIt; + // // End of The Lists!! @@ -131,6 +141,13 @@ private: /// .. and its associated convenient typedef typedef std::vector >::iterator HandleWidgetListIt; + /// LineWidget2 for line projection + vtkSmartPointer LineWidget; + /// List of Handles for the LineWidget2 + std::vector > HandleLineWidgetList; + /// .. and its associated convenient typedef + typedef std::vector >::iterator HandleLineWidgetListIt; + }; #endif /* VTKMRMLANNOTATIONDISPLAYABLEMANAGERHELPER_H_ */