Files
DCMV/src/src/Interaction/ActorDraggableInteractorStyle.cpp
2025-07-07 10:15:12 +08:00

660 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ActorDraggableInteractorStyle.h"
#include <vtkObjectFactory.h>
#include <vtkCollection.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkImageSlice.h>
#include <vtkImageMapper3D.h>
#include <vtkImageData.h>
#include <vtkProp.h>
#include <vtkPropCollection.h>
#include <vtkAssemblyPath.h>
#include <vtkRenderer.h>
#include <vtkCamera.h>
#include <vtkCellPicker.h>
#include <vtkCallbackCommand.h>
#include <vtkImageProperty.h>
#include <vtkPointData.h>
#include <vtkBoundingBox.h>
#include "Rendering/Core/DraggableActor.h"
#include "Rendering/Measure/CrossCursorAnnotationActor.h"
#include "Common/QGlobals.h"
vtkStandardNewMacro(ActorDraggableInteractorStyle)
ActorDraggableInteractorStyle::ActorDraggableInteractorStyle():vtkInteractorStyleImage()
{
HandleObservers = true;
this->AddObserver(vtkCommand::InteractionEvent, this, &ActorDraggableInteractorStyle::DispatchEvent);
}
ActorDraggableInteractorStyle::~ActorDraggableInteractorStyle() {
if (scalarProp) {
scalarProp->Delete();
scalarProp = nullptr;
}
if (CornerAnnotation) {
CornerAnnotation->UnRegister(this);
}
}
template<typename T>
void vtkValueMessageTemplate(vtkImageData *image, int *position, std::string &message) {
T *tuple = ((T *) image->GetScalarPointer(position));
if (!tuple) {
return;
}
int components = image->GetNumberOfScalarComponents();
for (int c = 0; c < components; ++c) {
message += vtkVariant(tuple[c]).ToString();
if (c != (components - 1)) {
message += ", ";
}
}
}
void ActorDraggableInteractorStyle::OnLeftButtonDown() {
this->InvokeEvent(AfterViewerClicked);
int x = this->Interactor->GetEventPosition()[0];
int y = this->Interactor->GetEventPosition()[1];
this->FindPokedRenderer(x, y);
if (this->CurrentRenderer == nullptr) {
return;
}
// Redefine this button to handle window/level
this->GrabFocus(this->EventCallbackCommand);
if (selectedProp) {
selectedProp->InvokeEvent(DraggableActor::DraggableActorEvents::UnSelectedEvent);
selectedProp = nullptr;
}
if (dragProp) {
selectedProp = dragProp;
selectedProp->InvokeEvent(DraggableActor::DraggableActorEvents::SelectedEvent);
#if VTK_MAJOR_VERSION<9
if (this->Interactor->GetRepeatCount()) {
dragProp->InvokeEvent(DraggableStyleEvents::PopPropEvent, nullptr);
}
#endif
DragStartOrigin[0] = x;
DragStartOrigin[1] = y;
this->StartDrag();
return;
}
#if VTK_MAJOR_VERSION<9
if (Interactor->GetRepeatCount()) {
if (measure) {
measure->SetPlacing(measure->onMeasureDoubleClick(this->Interactor));
if (!measure->isMeasurePlacing()) {
this->EndMeasure();
auto temp = measure;
measure = measure->GetNextMeasure();
if (!temp->Valid()) {
temp->ForceDelete();
temp = nullptr;
}
}
return;
}
if (!scalarProp){
this->InvokeEvent(DraggableStyleEvents::DoubleClickEvent, nullptr);
return;
}
}
#endif
if (measure) {
measure->SetPlacing(measure->onMeasureLeftButtonDown(this->Interactor));
if (this->State != VTKIS_MEASURE) this->StartMeasure();
return;
}
if (scalarProp) {
ScalarStartPosition[0] = x;
ScalarStartPosition[1] = y;
this->StartColorMapping();
return;
}
if (this->InteractionMode == VTKIS_IMAGE_WINDOWLEVEL) {
this->WindowLevelStartPosition[0] = x;
this->WindowLevelStartPosition[1] = y;
this->StartWindowLevel();
} else if (this->InteractionMode == VTKIS_IMAGE_ZOOM) {
this->DollyStartScale = this->CurrentRenderer->GetActiveCamera()->GetParallelScale();
this->StartDolly();
} else if (this->InteractionMode == VTKIS_IMAGE_PAN) {
this->StartPan();
} else if (this->InteractionMode == VTKIS_IMAGE_SLICING) {
this->StartSlice();
}
else if(this->InteractionMode == VTKIS_SYNCPOINT){
this->StartSyncSlicePoint();
}
}
#if VTK_MAJOR_VERSION>=9
void ActorDraggableInteractorStyle::OnLeftButtonDoubleClick() {
if (dragProp) {
dragProp->InvokeEvent(DraggableStyleEvents::PopPropEvent, nullptr);
}
if (measure) {
measure->SetPlacing(measure->onMeasureDoubleClick(this->Interactor));
if (!measure->isMeasurePlacing()) {
this->EndMeasure();
auto temp = measure;
measure = measure->GetNextMeasure();
if (!temp->Valid()) {
temp->ForceDelete();
temp = nullptr;
}
}
return;
}
if (!scalarProp) {
this->InvokeEvent(DraggableStyleEvents::DoubleClickEvent, nullptr);
return;
}
}
#endif
void ActorDraggableInteractorStyle::OnLeftButtonUp() {
switch (this->State) {
case VTKIS_DRAG:
DraggableActor::SafeDownCast(dragProp)->ApplyTransform();
this->EndDrag();
break;
case VTKIS_MEASURE:
measure->SetPlacing(measure->onMeasureLeftButtonUp(this->Interactor));
if (!measure->isMeasurePlacing()) {
this->EndMeasure();
auto temp = measure;
measure = measure->GetNextMeasure();
if (!temp->Valid()) {
temp->ForceDelete();
temp = nullptr;
}
}
break;
case VTKIS_COLORMAP:
this->EndColorMapping();
break;
case VTKIS_SYNCPOINT:
this->EndSyncSlicePoint();
break;
}
vtkInteractorStyleImage::OnLeftButtonUp();
}
void ActorDraggableInteractorStyle::OnRightButtonDown() {
this->InvokeEvent(AfterViewerClicked);
int x = this->Interactor->GetEventPosition()[0];
int y = this->Interactor->GetEventPosition()[1];
this->FindPokedRenderer(x, y);
if (this->CurrentRenderer == nullptr) {
return;
}
// Redefine this button to handle window/level
this->GrabFocus(this->EventCallbackCommand);
if (dragProp) {
dragProp->InvokeEvent(DraggableStyleEvents::RightButtonClickEvent, this->Interactor);
}
}
void ActorDraggableInteractorStyle::OnMouseMove() {
int x = this->Interactor->GetEventPosition()[0];
int y = this->Interactor->GetEventPosition()[1];
switch (this->State) {
case VTKIS_MEASURE:
MeasurePlace();
//照抄源码不是很清楚这句话的作用可能有加速处理用户输入事件的作用连续InteractionEvent处理连续操作避免外部判断
this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
break;
case VTKIS_DRAG:
Drag();
this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
break;
case VTKIS_NONE:
NoneStatePick();
this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
break;
case VTKIS_COLORMAP:
this->FindPokedRenderer(x, y);
this->ColorMapping();
this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
break;
case VTKIS_SYNCPOINT:
this->SyncSlicePoint();
this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
break;
}
vtkInteractorStyleImage::OnMouseMove();
}
void ActorDraggableInteractorStyle::OnChar() {
vtkInteractorStyleImage::OnChar();
vtkRenderWindowInteractor *rwi = this->Interactor;
std::string keySym = rwi->GetKeySym();
if (keySym == "Delete") {
if (selectedProp) {
this->InvokeEvent(DraggableStyleEvents::DeleteMeasureEvent, selectedProp);
}
}
}
void ActorDraggableInteractorStyle::NoneStatePick() {
if (AnnoHelper::GetVisibility()) {
int *pos = this->Interactor->GetEventPosition();
this->FindPokedRenderer(pos[0], pos[1]);
int result = picker->PickProp(pos[0], pos[1], this->CurrentRenderer);
if (result) {
vtkProp *obj = picker->GetViewProp();
if (scalarProp != obj && scalarProp) {
scalarProp = nullptr;
}
if (dragProp != obj && dragProp) {
DraggableActor::SafeDownCast(dragProp)->MouseLeave();
dragProp = nullptr;
}
if (obj->IsA("vtkImageSlice")) {
//active pick pixel
vtkNew<vtkCellPicker> p;
p->SetPickFromList(1);
p->AddPickList(obj);
p->Pick(pos[0], pos[1], 0.0, this->CurrentRenderer);
int *ijk = p->GetPointIJK();
//TODO: sometimes ijk will have value less than 0;need to fid out the reason
//Firstly: simple fix by set value to 0
for (int i = 0; i < 3; i++) {
ijk[i] = ijk[i] < 0 ? 0 : ijk[i];
}
std::string message = "X:";
message += vtkVariant(ijk[0]).ToString();
message += " Y:";
message += vtkVariant(ijk[1]).ToString();
message += " Z:";
message += vtkVariant(ijk[2]).ToString();
message += ", ";
message += vtkVariant(ijk[2]).ToString();
message += " Val:";
vtkImageData *imageData = vtkImageSlice::SafeDownCast(obj)->GetMapper()->GetInput();
switch (imageData->GetScalarType()) {
vtkTemplateMacro((vtkValueMessageTemplate<VTK_TT>(imageData, ijk, message)));
default:
return;
}
this->CornerAnnotation->SetText(BOTTOM_LEFT, message.c_str());
this->Interactor->Render();
return;
}
if (obj->IsA("DraggableActor")) {
//active highlight
if (dragProp != obj) {
dragProp = obj;
DraggableActor::SafeDownCast(dragProp)->MouseEntered();
}
}
if (obj->IsA("vtkScalarBarActor")) {
if (scalarProp != obj) {
scalarProp = obj;
}
}
}
//nopicked but moving
else if (dragProp) {
if (scalarProp) {
scalarProp = nullptr;
}
DraggableActor::SafeDownCast(dragProp)->MouseLeave();
} else {
if (scalarProp) {
scalarProp = nullptr;
}
dragProp = nullptr;
if (this->CornerAnnotation)this->CornerAnnotation->SetText(BOTTOM_LEFT, "");
}
}
if (this->CornerAnnotation)this->CornerAnnotation->SetText(BOTTOM_LEFT, "");
this->Interactor->Render();
}
void ActorDraggableInteractorStyle::MeasurePlace() {
measure->onMeasureMouseMove(this->Interactor);
}
void ActorDraggableInteractorStyle::Drag() {
int *pos = this->Interactor->GetEventPosition();
this->FindPokedRenderer(pos[0], pos[1]);
DraggableActor::SafeDownCast(dragProp)->Transform(pos[0] - DragStartOrigin[0], pos[1] - DragStartOrigin[1]);
this->Interactor->Render();
}
void ActorDraggableInteractorStyle::ActiveMeasure(Measure *m) {
if (this->measure ) {
this->measure->onTerminate(this->Interactor);
this->measure->ForceDelete();
}
this->measure = m;
}
void ActorDraggableInteractorStyle::UnActiveMeasure() {
if (this->measure) {
this->measure->onTerminate(this->Interactor);
this->measure->ForceDelete();
this->measure = nullptr;
}
this->EndMeasure();
this->measure = nullptr;
}
void ActorDraggableInteractorStyle::Slice() {
if (this->CurrentRenderer == nullptr) {
return;
}
vtkRenderWindowInteractor *rwi = this->Interactor;
int dy = (-rwi->GetEventPosition()[1] + rwi->GetLastEventPosition()[1]) / 2;
// vtkCamera *camera = this->CurrentRenderer->GetActiveCamera();
//
//
// // scale the interaction by the height of the viewport
// double viewportHeight = 0.0;
// viewportHeight = camera->GetParallelScale();
//
//
// int *size = this->CurrentRenderer->GetSize();
// double delta = dy*viewportHeight/size[1];
// int dSlice = -(int)round(delta);
// printf("dy:%d,delta:%f,dslice:%d\r\n",dy,delta,dSlice);
void *p = &dy;
this->InvokeEvent(SliceEvent, p);
}
void ActorDraggableInteractorStyle::StartSyncSlicePoint()
{
this->StartState(VTKIS_SYNCPOINT);
vtkNew<CrossCursorAnnotationActor> cross;
int *pos = this->Interactor->GetEventPosition();
this->FindPokedRenderer(pos[0], pos[1]);
this->CurrentRenderer->SetDisplayPoint(pos[0], pos[1], 0.0);
this->CurrentRenderer->DisplayToWorld();
cross->SetRenderer(this->CurrentRenderer);
cross->Transform(pos[0] , pos[1] );
dragProp = cross;
this->InvokeEvent(StartSyncSlicePointEvent,nullptr);
SyncSlicePoint();
this->Interactor->Render();
}
void ActorDraggableInteractorStyle::EndSyncSlicePoint()
{
if (this->State != VTKIS_SYNCPOINT) {
return;
}
this->StopState();
DraggableActor::SafeDownCast(dragProp)->SetRenderer(nullptr);
// dragProp->Delete();
dragProp = nullptr;
this->InvokeEvent(EndSyncSlicePointEvent,nullptr);
this->Interactor->Render();
}
void ActorDraggableInteractorStyle::SyncSlicePoint()
{
int *pos = this->Interactor->GetEventPosition();
this->FindPokedRenderer(pos[0], pos[1]);
CrossCursorAnnotationActor::SafeDownCast(dragProp)->Transform(pos[0] , pos[1] );
this->Interactor->Render();
this->InvokeEvent(SyncSlicePointEvent,CrossCursorAnnotationActor::SafeDownCast(dragProp)->GetWorldSlicePoint());
}
void ActorDraggableInteractorStyle::AdjustSyncCrossPoint(double* aPoint)
{
if(dragProp == nullptr)
{
if (CurrentRenderer == nullptr) return;
if (aPoint == nullptr) return;
if (CurrentImageSlice == nullptr) return;
// if (box.ContainsPoint(aPoint))
{
vtkNew<CrossCursorAnnotationActor> cross;
cross->SetRenderer(this->CurrentRenderer);
cross->Transform(aPoint[0] , aPoint[1] );
dragProp = cross;
}
}
else{
if (aPoint == nullptr)
{
DraggableActor::SafeDownCast(dragProp)->SetRenderer(nullptr);
// dragProp->Delete();
dragProp = nullptr;
}
else{
CrossCursorAnnotationActor::SafeDownCast(dragProp)->Transform(aPoint[0] , aPoint[1] );
this->Interactor->Render();
}
}
}
void ActorDraggableInteractorStyle::StartPan() {
vtkInteractorStyle::StartPan();
vtkCamera *camera = this->CurrentRenderer->GetActiveCamera();
camera->GetFocalPoint(PanStartOrigin);
}
void ActorDraggableInteractorStyle::ColorMapping() {
vtkRenderWindowInteractor *rwi = this->Interactor;
this->ScalarCurrentPosition[1] = rwi->GetEventPosition()[1];
double total = 0.01 * ((this->ScalarCurrentPosition[1] - this->ScalarStartPosition[1]) / scalarSensitivity);
double left = total - ConsumedOpacity;
if (abs(left) >= 0.01) {
OpacityTrigger = true;
this->InvokeEvent(ScalarOpacityEvent, &left);
ConsumedOpacity += left;
}
}
void ActorDraggableInteractorStyle::EndColorMapping() {
if (!OpacityTrigger) {
this->InvokeEvent(ScalarShiftEvent, nullptr);
}
if (this->State != VTKIS_COLORMAP) {
return;
}
this->StopState();
}
void ActorDraggableInteractorStyle::EndPan() {
if (this->State != VTKIS_PAN) return;
if (this->HandleObservers) {
vtkCamera *camera = this->CurrentRenderer->GetActiveCamera();
double *nf = camera->GetFocalPoint();
double calldata[6] = {PanStartOrigin[0], PanStartOrigin[1], PanStartOrigin[2], nf[0], nf[1], nf[2]};
this->InvokeEvent(vtkCommand::EventIds::EndPanEvent, calldata);
}
this->StopState();
}
void ActorDraggableInteractorStyle::EndDolly() {
if (this->State != VTKIS_DOLLY) {
return;
}
this->StopState();
if (this->CurrentRenderer == nullptr) {
return;
}
//激发缩放完成事件
if (this->HandleObservers) {
vtkCamera *camera = this->CurrentRenderer->GetActiveCamera();
double result[2] = {0.0, 0.0};
//image 必然是ParallelProjection
result[0] = this->DollyStartScale;
result[1] = camera->GetParallelScale();
this->InvokeEvent(DraggableStyleEvents::EndDollyEvent, result);
}
}
void ActorDraggableInteractorStyle::WindowLevel() {
vtkRenderWindowInteractor *rwi = this->Interactor;
this->WindowLevelCurrentPosition[0] = rwi->GetEventPosition()[0];
this->WindowLevelCurrentPosition[1] = rwi->GetEventPosition()[1];
if (this->CurrentImageProperty) {
int *size = this->CurrentRenderer->GetSize();
double window = this->WindowLevelInitial[0];
double level = this->WindowLevelInitial[1];
// Compute normalized delta
double dx = (this->WindowLevelCurrentPosition[0] -
this->WindowLevelStartPosition[0]) * 4.0 / size[0];
double dy = (this->WindowLevelStartPosition[1] -
this->WindowLevelCurrentPosition[1]) * 4.0 / size[1];
// Scale by current values
if (fabs(window) > 0.01) {
dx = dx * window;
} else {
dx = dx * (window < 0 ? -0.01 : 0.01);
}
if (fabs(level) > 0.01) {
dy = dy * level;
} else {
dy = dy * (level < 0 ? -0.01 : 0.01);
}
// Abs so that direction does not flip
if (window < 0.0) {
dx = -1 * dx;
}
if (level < 0.0) {
dy = -1 * dy;
}
// Compute new window level
double newWindow = dx + window;
double newLevel = level - dy;
if (newWindow < 0.01) {
newWindow = 0.01;
}
this->CurrentImageProperty->SetColorWindow(newWindow);
this->CurrentImageProperty->SetColorLevel(newLevel);
this->Interactor->Render();
if (this->HandleObservers &&
this->HasObserver(vtkCommand::WindowLevelEvent)) {
this->InvokeEvent(vtkCommand::WindowLevelEvent, this);
}
}
}
void ActorDraggableInteractorStyle::EndWindowLevel() {
if (this->State != VTKIS_WINDOW_LEVEL) {
return;
}
if (this->HandleObservers) {
double calldata[2] = {this->CurrentImageProperty->GetColorWindow(),
this->CurrentImageProperty->GetColorLevel()};
this->InvokeEvent(vtkCommand::EndWindowLevelEvent, calldata);
}
this->StopState();
}
//重写部分逻辑在取imageProperty的时候把ImageSlice也取了。
void ActorDraggableInteractorStyle::SetCurrentImageNumber(int i) {
// return;
this->CurrentImageNumber = i;
if (!this->CurrentRenderer) {
return;
}
vtkPropCollection *props = this->CurrentRenderer->GetViewProps();
vtkProp *prop = nullptr;
vtkAssemblyPath *path;
vtkImageSlice *imageProp = nullptr;
vtkCollectionSimpleIterator pit;
for (int k = 0; k < 2; k++) {
int j = 0;
for (props->InitTraversal(pit); (prop = props->GetNextProp(pit));) {
bool foundImageProp = false;
for (prop->InitPathTraversal(); (path = prop->GetNextPath());) {
vtkProp *tryProp = path->GetLastNode()->GetViewProp();
imageProp = vtkImageSlice::SafeDownCast(tryProp);
if (imageProp) {
if (j == i) {
foundImageProp = true;
break;
}
imageProp = nullptr;
j++;
}
}
if (foundImageProp) {
break;
}
}
if (i < 0) {
i += j;
}
}
vtkImageProperty *property = nullptr;
if (imageProp) {
property = imageProp->GetProperty();
if (imageProp != this->CurrentImageSlice) {
if (this->CurrentImageSlice) {
this->CurrentImageSlice->Delete();
}
this->CurrentImageSlice = imageProp;
if (this->CurrentImageSlice) {
this->CurrentImageSlice->Register(this);
}
}
}
if (property != this->CurrentImageProperty) {
if (this->CurrentImageProperty) {
this->CurrentImageProperty->Delete();
}
this->CurrentImageProperty = property;
if (this->CurrentImageProperty) {
this->CurrentImageProperty->Register(this);
}
}
}
void ActorDraggableInteractorStyle::DispatchEvent() {
}