Orientation marker click change volume view orientation.

This commit is contained in:
Krad
2022-10-28 13:56:00 +08:00
parent dd64953876
commit 01736047f7
4 changed files with 205 additions and 17 deletions

View File

@@ -20,11 +20,11 @@
#include <vtkCornerAnnotation.h> #include <vtkCornerAnnotation.h>
#include <vtkPlane.h> #include <vtkPlane.h>
#include <vtkBoundingBox.h> #include <vtkBoundingBox.h>
#include <vtkOrientationMarkerWidget.h>
#include <vtkAnnotatedCubeActor.h> #include <vtkAnnotatedCubeActor.h>
#include <vtkVectorText.h>
#include "Interaction/VolumeInteractorStyle.h" #include "Interaction/VolumeInteractorStyle.h"
#include "Rendering/Widget/ClickableOrientationMarkerWidget.h"
#include "IO/DICOM/ExtendMedicalImageProperties.h" #include "IO/DICOM/ExtendMedicalImageProperties.h"
namespace { namespace {
@@ -36,6 +36,36 @@ namespace {
Superior, Superior,
Inferior 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); vtkStandardNewMacro(VolumeRenderingViewer);
@@ -50,7 +80,7 @@ VolumeRenderingViewer::VolumeRenderingViewer()
, InteractorStyle(nullptr) , InteractorStyle(nullptr)
, Interactor(nullptr) , Interactor(nullptr)
, firstRender(true) , firstRender(true)
, OrientationMarker(vtkOrientationMarkerWidget::New()){ , OrientationMarker(ClickableOrientationMarkerWidget::New()){
if (gpuMode){ if (gpuMode){
auto mapper = vtkGPUVolumeRayCastMapper::New(); auto mapper = vtkGPUVolumeRayCastMapper::New();
mapper->SetUseJittering(1); mapper->SetUseJittering(1);
@@ -145,7 +175,6 @@ void VolumeRenderingViewer::InstallPipeline() {
if (this->Interactor) { if (this->Interactor) {
if (!this->InteractorStyle) { if (!this->InteractorStyle) {
auto style = VolumeInteractorStyle::New(); auto style = VolumeInteractorStyle::New();
// style->SetInteractionMode(1024);
this->InteractorStyle = style; this->InteractorStyle = style;
this->InteractorStyle->AddObserver(vtkCommand::StartWindowLevelEvent,this, this->InteractorStyle->AddObserver(vtkCommand::StartWindowLevelEvent,this,
&VolumeRenderingViewer::DecreaseMaximumImageSampleDistance); &VolumeRenderingViewer::DecreaseMaximumImageSampleDistance);
@@ -261,11 +290,13 @@ void VolumeRenderingViewer::Render() {
this->OrientationMarker->RemoveAllObservers(); this->OrientationMarker->RemoveAllObservers();
this->OrientationMarker->SetInteractor(this->Interactor); this->OrientationMarker->SetInteractor(this->Interactor);
this->OrientationMarker->EnabledOn(); this->OrientationMarker->EnabledOn();
this->OrientationMarker->InteractiveOff();
this->RenderWindow->AddObserver(vtkCommand::WindowResizeEvent, this, &VolumeRenderingViewer::resizeOrientationMarker); this->RenderWindow->AddObserver(vtkCommand::WindowResizeEvent, this, &VolumeRenderingViewer::resizeOrientationMarker);
Render(); Render();
} }
Interactor->Render(); Interactor->Render();
renderAnnotation();
} }
} }
@@ -318,14 +349,14 @@ void VolumeRenderingViewer::renderAnnotation() {
void VolumeRenderingViewer::GetDirectionString(const double *directionVector, std::string &str) const { void VolumeRenderingViewer::GetDirectionString(const double *directionVector, std::string &str) const {
str.clear(); str.clear();
char dV [3] = {' ', ' ', ' '}; char dV [3] = {' ', ' ', ' '};
if (fabs(directionVector[0]) > 0.5){ if (fabs(directionVector[0]) > 0.6){
dV[0] = directionVector[0] > 0 ? 'L' : 'R'; dV[0] = directionVector[0] > 0 ? 'L' : 'R';
} }
if (fabs(directionVector[1])>0.5){ if (fabs(directionVector[1]) > 0.6){
dV[1] = directionVector[1]>0?'P':'A'; dV[1] = directionVector[1]> 0 ? 'P' : 'A';
} }
if (fabs(directionVector[2])>0.5){ if (fabs(directionVector[2]) > 0.6){
dV[2] =directionVector[2]>0?'H':'F'; dV[2] = directionVector[2] > 0 ? 'H' : 'F';
} }
if (fabs(directionVector[2]) > fabs(directionVector[1])) std::swap(dV[1], dV[2]); 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]); 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; worldDirection = 0;
return directionVector[0] > 0 ? 'L' : 'R'; return directionVector[0] > 0 ? 'L' : 'R';
} }
if (fabs(directionVector[1])>0.7){ if (fabs(directionVector[1]) > 0.7){
worldDirection = 1; 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; 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->SetZFaceTextRotation(-90);
} }
} }
cube->SetTextEdgesVisibility(0);
cube->GetTextEdgesProperty()->SetColor(1.0,1.0,.0); cube->GetZMinusFaceProperty()->SetColor(1.0,0.863,.3);
cube->GetTextEdgesProperty()->SetLineWidth(2.0); cube->GetZPlusFaceProperty()->SetColor(1.0,0.863,.3);
cube->GetCubeProperty()->SetColor(.0,.0,1.0); 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); OrientationMarker->SetOrientationMarker(cube);
cube->AddObserver(vtkCommand::PickEvent,this,&VolumeRenderingViewer::pressedOrientationMarker);
} }
//change to WToM //change to WToM
OrientationMatrix->Invert(); OrientationMatrix->Invert();
@@ -540,3 +585,15 @@ void VolumeRenderingViewer::resizeOrientationMarker() {
int *size = this->Renderer->GetSize(); 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); 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<vtkVariant*>(data);
if (var){
auto cube = vtkAnnotatedCubeActor::SafeDownCast(OrientationMarker->GetOrientationMarker());
if (cube){
int v = GetViewDirection(cube,var->ToInt());
SetViewDirection(v);
Render();
}
}
}

View File

@@ -127,6 +127,7 @@ private:
int a; int a;
return GetDirectionChar(directionVector, a); return GetDirectionChar(directionVector, a);
} }
void pressedOrientationMarker(vtkObject*, unsigned long, void*);
void resizeOrientationMarker(); void resizeOrientationMarker();
}; };

View File

@@ -0,0 +1,99 @@
//
// Created by Krad on 2022/10/26.
//
#include "ClickableOrientationMarkerWidget.h"
#include <vtkObjectFactory.h>
#include <vtkNew.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkCallbackCommand.h>
#include <vtkRenderer.h>
#include <vtkPropPicker.h>
#include <vtkCamera.h>
#include <vtkPlane.h>
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<int>( vp[0] ), static_cast<int>( vp[1] ) };
int pos2[2] = { static_cast<int>( vp[2] ), static_cast<int>( 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<vtkPlane> 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:" <<faceIndex);
obj->InvokeEvent(vtkCommand::PickEvent,&PressedFaceIndex);
}
}

View File

@@ -0,0 +1,31 @@
//
// Created by Krad on 2022/10/26.
//
#ifndef OMEGAV_CLICKABLEORIENTATIONMARKERWIDGET_H
#define OMEGAV_CLICKABLEORIENTATIONMARKERWIDGET_H
#include <vtkOrientationMarkerWidget.h>
#include <vtkVariant.h>
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