diff --git a/src/src/Rendering/Viewer/VolumeRenderingViewer.cpp b/src/src/Rendering/Viewer/VolumeRenderingViewer.cpp index 2f86ff9..408a6e9 100644 --- a/src/src/Rendering/Viewer/VolumeRenderingViewer.cpp +++ b/src/src/Rendering/Viewer/VolumeRenderingViewer.cpp @@ -20,11 +20,11 @@ #include #include #include -#include #include - +#include #include "Interaction/VolumeInteractorStyle.h" +#include "Rendering/Widget/ClickableOrientationMarkerWidget.h" #include "IO/DICOM/ExtendMedicalImageProperties.h" namespace { @@ -36,6 +36,36 @@ namespace { Superior, Inferior }; + const char VIEW_FIRST_LETTER[7] = "APLRHF"; +} + +int GetViewDirection(vtkAnnotatedCubeActor* cube,int orientation){ + char faceText = 'A'; + switch (orientation){ + case 0: + faceText = cube->GetXMinusFaceText()[0]; + break; + case 3: + faceText = cube->GetXPlusFaceText()[0]; + break; + case 1: + faceText = cube->GetYMinusFaceText()[0]; + break; + case 4: + faceText = cube->GetYPlusFaceText()[0]; + break; + case 2: + faceText = cube->GetZMinusFaceText()[0]; + break; + case 5: + faceText = cube->GetZPlusFaceText()[0]; + break; + } + for (int i = 0; i < 6; ++i) { + if (VIEW_FIRST_LETTER[i]==faceText) + return i; + } + return 0; } vtkStandardNewMacro(VolumeRenderingViewer); @@ -50,7 +80,7 @@ VolumeRenderingViewer::VolumeRenderingViewer() , InteractorStyle(nullptr) , Interactor(nullptr) , firstRender(true) -, OrientationMarker(vtkOrientationMarkerWidget::New()){ +, OrientationMarker(ClickableOrientationMarkerWidget::New()){ if (gpuMode){ auto mapper = vtkGPUVolumeRayCastMapper::New(); mapper->SetUseJittering(1); @@ -145,7 +175,6 @@ void VolumeRenderingViewer::InstallPipeline() { if (this->Interactor) { if (!this->InteractorStyle) { auto style = VolumeInteractorStyle::New(); -// style->SetInteractionMode(1024); this->InteractorStyle = style; this->InteractorStyle->AddObserver(vtkCommand::StartWindowLevelEvent,this, &VolumeRenderingViewer::DecreaseMaximumImageSampleDistance); @@ -261,11 +290,13 @@ void VolumeRenderingViewer::Render() { this->OrientationMarker->RemoveAllObservers(); this->OrientationMarker->SetInteractor(this->Interactor); this->OrientationMarker->EnabledOn(); - this->OrientationMarker->InteractiveOff(); + this->RenderWindow->AddObserver(vtkCommand::WindowResizeEvent, this, &VolumeRenderingViewer::resizeOrientationMarker); Render(); + } Interactor->Render(); + renderAnnotation(); } } @@ -318,14 +349,14 @@ void VolumeRenderingViewer::renderAnnotation() { void VolumeRenderingViewer::GetDirectionString(const double *directionVector, std::string &str) const { str.clear(); char dV [3] = {' ', ' ', ' '}; - if (fabs(directionVector[0]) > 0.5){ + if (fabs(directionVector[0]) > 0.6){ dV[0] = directionVector[0] > 0 ? 'L' : 'R'; } - if (fabs(directionVector[1])>0.5){ - dV[1] = directionVector[1]>0?'P':'A'; + if (fabs(directionVector[1]) > 0.6){ + dV[1] = directionVector[1]> 0 ? 'P' : 'A'; } - if (fabs(directionVector[2])>0.5){ - dV[2] =directionVector[2]>0?'H':'F'; + if (fabs(directionVector[2]) > 0.6){ + dV[2] = directionVector[2] > 0 ? 'H' : 'F'; } if (fabs(directionVector[2]) > fabs(directionVector[1])) std::swap(dV[1], dV[2]); if (fabs(directionVector[1]) > fabs(directionVector[0])) std::swap(dV[0], dV[1]); @@ -348,13 +379,13 @@ char VolumeRenderingViewer::GetDirectionChar(const double *directionVector , int worldDirection = 0; return directionVector[0] > 0 ? 'L' : 'R'; } - if (fabs(directionVector[1])>0.7){ + if (fabs(directionVector[1]) > 0.7){ worldDirection = 1; - return directionVector[1]>0?'P':'A'; + return directionVector[1] > 0 ? 'P' : 'A'; } - if (fabs(directionVector[2])>0.7){ + if (fabs(directionVector[2]) > 0.7){ worldDirection = 2; - return directionVector[2]>0?'H':'F'; + return directionVector[2] > 0 ? 'H' : 'F'; } } @@ -419,11 +450,25 @@ void VolumeRenderingViewer::SetCoordsTransformMatrix(ExtendMedicalImagePropertie cube->SetZFaceTextRotation(-90); } } + cube->SetTextEdgesVisibility(0); - cube->GetTextEdgesProperty()->SetColor(1.0,1.0,.0); - cube->GetTextEdgesProperty()->SetLineWidth(2.0); - cube->GetCubeProperty()->SetColor(.0,.0,1.0); + cube->GetZMinusFaceProperty()->SetColor(1.0,0.863,.3); + cube->GetZPlusFaceProperty()->SetColor(1.0,0.863,.3); + cube->GetYMinusFaceProperty()->SetColor(.839,.784,0.6); + cube->GetYPlusFaceProperty()->SetColor(.839,.784,0.6); + cube->GetXMinusFaceProperty()->SetColor(.882,.3,0.2); + cube->GetXPlusFaceProperty()->SetColor(.882,.3,0.2); + cube->GetTextEdgesProperty()->SetEdgeVisibility(0); +// cube->GetTextEdgesProperty()->SetEdgeColor(0.149,0.737,.835); +// cube->GetTextEdgesProperty()->SetColor(1.0,1.0,.0); +// cube->GetTextEdgesProperty()->SetLineWidth(2.0); +// cube->GetCubeProperty()->SetEdgeColor(1.0,1.0,.0); +// cube->GetCubeProperty()->SetEdgeVisibility(1); + cube->GetCubeProperty()->SetColor(0.11,0.471,.539); + cube->SetPickable(true); + vtkVectorText* text = vtkVectorText::New(); OrientationMarker->SetOrientationMarker(cube); + cube->AddObserver(vtkCommand::PickEvent,this,&VolumeRenderingViewer::pressedOrientationMarker); } //change to WToM OrientationMatrix->Invert(); @@ -540,3 +585,15 @@ void VolumeRenderingViewer::resizeOrientationMarker() { int *size = this->Renderer->GetSize(); this->OrientationMarker->SetViewport(1.0-(80.0/(double)size[0]),1.0-(80.0/(double)size[1]),1.0,1.0); } + +void VolumeRenderingViewer::pressedOrientationMarker(vtkObject* sender, unsigned long event, void* data) { + vtkVariant* var = static_cast(data); + if (var){ + auto cube = vtkAnnotatedCubeActor::SafeDownCast(OrientationMarker->GetOrientationMarker()); + if (cube){ + int v = GetViewDirection(cube,var->ToInt()); + SetViewDirection(v); + Render(); + } + } +} diff --git a/src/src/Rendering/Viewer/VolumeRenderingViewer.h b/src/src/Rendering/Viewer/VolumeRenderingViewer.h index 9125d48..d706867 100644 --- a/src/src/Rendering/Viewer/VolumeRenderingViewer.h +++ b/src/src/Rendering/Viewer/VolumeRenderingViewer.h @@ -127,6 +127,7 @@ private: int a; return GetDirectionChar(directionVector, a); } + void pressedOrientationMarker(vtkObject*, unsigned long, void*); void resizeOrientationMarker(); }; diff --git a/src/src/Rendering/Widget/ClickableOrientationMarkerWidget.cpp b/src/src/Rendering/Widget/ClickableOrientationMarkerWidget.cpp new file mode 100644 index 0000000..36b0900 --- /dev/null +++ b/src/src/Rendering/Widget/ClickableOrientationMarkerWidget.cpp @@ -0,0 +1,99 @@ +// +// Created by Krad on 2022/10/26. +// + +#include "ClickableOrientationMarkerWidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +vtkStandardNewMacro(ClickableOrientationMarkerWidget) + +ClickableOrientationMarkerWidget::ClickableOrientationMarkerWidget():vtkOrientationMarkerWidget() +, picker(vtkPropPicker::New()) +{ + +} + +ClickableOrientationMarkerWidget::~ClickableOrientationMarkerWidget() { + picker->Delete(); +} + +bool checkInCube(double* pt){ + return fabs(pt[0])<0.5015 && fabs(pt[1])<0.5015 && fabs(pt[2])<0.5015; +} + +void ClickableOrientationMarkerWidget::OnLeftButtonDown() { + // We're only here if we are enabled + int X = this->Interactor->GetEventPosition()[0]; + int Y = this->Interactor->GetEventPosition()[1]; + + // are we over the widget? + double vp[4]; + Renderer->GetViewport( vp ); + + this->Renderer->NormalizedDisplayToDisplay( vp[0], vp[1] ); + this->Renderer->NormalizedDisplayToDisplay( vp[2], vp[3] ); + + int pos1[2] = { static_cast( vp[0] ), static_cast( vp[1] ) }; + int pos2[2] = { static_cast( vp[2] ), static_cast( vp[3] ) }; + + this->StartPosition[0] = X; + this->StartPosition[1] = Y; + + this->State = this->ComputeStateBasedOnPosition( X, Y, pos1, pos2 ); +// this->SetCursor( this->State ); + + if (this->State == vtkOrientationMarkerWidget::Outside) + { + return; + } + + //add click actor event + int result = picker->PickProp(X,Y,this->Renderer); + if (result){ + vtkProp *obj = picker->GetViewProp(); + this->Renderer->SetDisplayPoint(X,Y,0); + this->Renderer->DisplayToWorld(); + double pt[4] = {.0,.0,.0,.0}; + this->Renderer->GetWorldPoint(pt); + double proj[3] = {.0,.0,.0}; + this->Renderer->GetActiveCamera()->GetDirectionOfProjection(proj); + double pt2[3] = {pt[0] + proj[0]*10, pt[1] + proj[1]*10, pt[2] + proj[2]*10}; + double* bounds = obj->GetBounds(); + double ptMin[3]={bounds[0],bounds[2],bounds[4]}; + double ptMax[3]={bounds[1],bounds[3],bounds[5]}; + double* originPts[2] = {ptMin,ptMax}; + double t; + double intersectPt[3] = {.0, .0, .0}; + double dis = VTK_DOUBLE_MAX; + int faceIndex = -1; + vtkNew plane; + for (int i = 0; i < 2; ++i) { + plane->SetOrigin(originPts[i]); + for (int j = 0; j < 3; ++j) { + double orientation[3] = {.0, .0, .0}; + orientation[j] = 1.0; + plane->SetNormal(orientation); + if(plane->IntersectWithLine(pt, pt2, t, intersectPt)) { + if (!checkInCube(intersectPt)) continue; + double d = vtkMath::Distance2BetweenPoints(pt, intersectPt); + if (d < dis) { + dis = d; + faceIndex = i * 3 + j; + } + } + } + } + PressedFaceIndex = faceIndex; + vtkErrorMacro("face index:" <InvokeEvent(vtkCommand::PickEvent,&PressedFaceIndex); + } +} + diff --git a/src/src/Rendering/Widget/ClickableOrientationMarkerWidget.h b/src/src/Rendering/Widget/ClickableOrientationMarkerWidget.h new file mode 100644 index 0000000..c1c374f --- /dev/null +++ b/src/src/Rendering/Widget/ClickableOrientationMarkerWidget.h @@ -0,0 +1,31 @@ +// +// Created by Krad on 2022/10/26. +// + +#ifndef OMEGAV_CLICKABLEORIENTATIONMARKERWIDGET_H +#define OMEGAV_CLICKABLEORIENTATIONMARKERWIDGET_H + +#include +#include + +class vtkPropPicker; + +class ClickableOrientationMarkerWidget: public vtkOrientationMarkerWidget { +public: + static ClickableOrientationMarkerWidget* New(); + vtkTypeMacro(ClickableOrientationMarkerWidget, vtkOrientationMarkerWidget); +protected: + ClickableOrientationMarkerWidget(); + ~ClickableOrientationMarkerWidget() override; + void OnLeftButtonDown()override; + void OnLeftButtonUp() override {}; + void OnMouseMove() override {}; +private: + ClickableOrientationMarkerWidget(const ClickableOrientationMarkerWidget&) = delete; + void operator=(const ClickableOrientationMarkerWidget&) = delete; + vtkPropPicker* picker; + vtkVariant PressedFaceIndex; +}; + + +#endif //OMEGAV_CLICKABLEORIENTATIONMARKERWIDGET_H