diff --git a/CMakeLists.txt b/CMakeLists.txt index 7475d71..750f1f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ include_directories( ) #set(VTK_DIR "D:/Libs/binary/VTK8.1.2/lib/cmake/vtk-8.2") -set(VTK_DIR "D:/Libs/binary/VTK9.2.2/lib/cmake/vtk-9.2") +set(VTK_DIR "D:/Libs/binary/VTK9.2.2win11/lib/cmake/vtk-9.2") set(Qt5_DIR "D:/Qt/Qt5.12.0/5.12.0/msvc2017_64/lib/cmake/Qt5") set(DCMTK_DIR "D:/Libs/binary/DCMTK/cmake") diff --git a/src/Icon/crosshair.png b/src/Icon/crosshair.png new file mode 100644 index 0000000..4ff4a70 Binary files /dev/null and b/src/Icon/crosshair.png differ diff --git a/src/QDicomViewer.qrc b/src/QDicomViewer.qrc index f8bbca3..ab665d8 100644 --- a/src/QDicomViewer.qrc +++ b/src/QDicomViewer.qrc @@ -58,6 +58,7 @@ Icon/Settings.png Icon/preset.png Icon/diameter.png + Icon/crosshair.png Icon/pq/pqBold24.png diff --git a/src/src/Common/QGlobals.h b/src/src/Common/QGlobals.h index 80e3efc..a977bd7 100644 --- a/src/src/Common/QGlobals.h +++ b/src/src/Common/QGlobals.h @@ -83,6 +83,7 @@ enum AnnotationActorType EllipseAnn, DiameterAnn, TextAnn, + SyncPoint }; enum AnnotationDeleteType{ DeleteSelectedAnn, diff --git a/src/src/Interaction/ActorDraggableInteractorStyle.cpp b/src/src/Interaction/ActorDraggableInteractorStyle.cpp index 39a3eef..4950f9b 100644 --- a/src/src/Interaction/ActorDraggableInteractorStyle.cpp +++ b/src/src/Interaction/ActorDraggableInteractorStyle.cpp @@ -3,7 +3,9 @@ #include #include +#include #include + #include #include #include @@ -16,8 +18,11 @@ #include #include #include +#include + #include "Rendering/Core/DraggableActor.h" +#include "Rendering/Measure/CrossCursorAnnotationActor.h" #include "Common/QGlobals.h" vtkStandardNewMacro(ActorDraggableInteractorStyle) @@ -126,6 +131,9 @@ void ActorDraggableInteractorStyle::OnLeftButtonDown() { } else if (this->InteractionMode == VTKIS_IMAGE_SLICING) { this->StartSlice(); } + else if(this->InteractionMode == VTKIS_SYNCPOINT){ + this->StartSyncSlicePoint(); + } } @@ -175,6 +183,9 @@ void ActorDraggableInteractorStyle::OnLeftButtonUp() { case VTKIS_COLORMAP: this->EndColorMapping(); break; + case VTKIS_SYNCPOINT: + this->EndSyncSlicePoint(); + break; } vtkInteractorStyleImage::OnLeftButtonUp(); } @@ -219,6 +230,10 @@ void ActorDraggableInteractorStyle::OnMouseMove() { this->ColorMapping(); this->InvokeEvent(vtkCommand::InteractionEvent, nullptr); break; + case VTKIS_SYNCPOINT: + this->SyncSlicePoint(); + this->InvokeEvent(vtkCommand::InteractionEvent, nullptr); + break; } vtkInteractorStyleImage::OnMouseMove(); @@ -368,6 +383,74 @@ void ActorDraggableInteractorStyle::Slice() { this->InvokeEvent(SliceEvent, p); } +void ActorDraggableInteractorStyle::StartSyncSlicePoint() +{ + this->StartState(VTKIS_SYNCPOINT); + vtkNew 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 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(); diff --git a/src/src/Interaction/ActorDraggableInteractorStyle.h b/src/src/Interaction/ActorDraggableInteractorStyle.h index 2e97854..bcefbef 100644 --- a/src/src/Interaction/ActorDraggableInteractorStyle.h +++ b/src/src/Interaction/ActorDraggableInteractorStyle.h @@ -115,6 +115,7 @@ vtkTypeMacro(ActorDraggableInteractorStyle, vtkInteractorStyleImage); { return CurrentImageSlice; } + void AdjustSyncCrossPoint(double* aPoint); protected: @@ -170,6 +171,12 @@ protected: void NoneStatePick(); + void StartSyncSlicePoint(); + + void EndSyncSlicePoint() ; + + void SyncSlicePoint(); + void DispatchEvent(); diff --git a/src/src/Rendering/Core/RenderingDefines.h b/src/src/Rendering/Core/RenderingDefines.h index b7298a7..42f04bd 100644 --- a/src/src/Rendering/Core/RenderingDefines.h +++ b/src/src/Rendering/Core/RenderingDefines.h @@ -8,6 +8,7 @@ #define VTKIS_DRAG 33 #define VTKIS_MEASURE 36 #define VTKIS_COLORMAP 37 +#define VTKIS_SYNCPOINT 38 #define VTKIS_IMAGE_PAN 5 #define VTKIS_IMAGE_ZOOM 6 #define VTKIS_IMAGE_WINDOWLEVEL 7 @@ -34,7 +35,10 @@ enum DraggableStyleEvents { RightButtonClickEvent, ScalarOpacityEvent, ScalarShiftEvent, - AfterViewerClicked + AfterViewerClicked, + StartSyncSlicePointEvent, + SyncSlicePointEvent, + EndSyncSlicePointEvent }; diff --git a/src/src/Rendering/Measure/CrossCursorAnnotationActor.cpp b/src/src/Rendering/Measure/CrossCursorAnnotationActor.cpp new file mode 100644 index 0000000..0d0fbc8 --- /dev/null +++ b/src/src/Rendering/Measure/CrossCursorAnnotationActor.cpp @@ -0,0 +1,72 @@ +#include "CrossCursorAnnotationActor.h" + +#include "Rendering/Source/vtkCrossCursorSource.h" +#include +#include +#include +#include +#include +#include + + +vtkStandardNewMacro(CrossCursorAnnotationActor) + +CrossCursorAnnotationActor::CrossCursorAnnotationActor() + : DraggableActor(){ + BaseDataPoints->SetNumberOfPoints(1); + BaseDataPoints->SetPoint(0, 0, 0, 0); + + renderPoints->SetNumberOfPoints(1); + renderPoints->SetPoint(0, 0, 0, 0); + + this->AddObserver(DraggableActorEvents::DragEvent, this, &CrossCursorAnnotationActor::selfDragCb); + this->actor2D->GetProperty()->SetLineWidth(1.0); + this->shadow2D->GetProperty()->SetLineWidth(2); + this->shadow2D->SetVisibility(1); + +} + +CrossCursorAnnotationActor::~CrossCursorAnnotationActor() { + +} + +void CrossCursorAnnotationActor::SetWorldPosition1(double x, double y, double z) { + BaseDataPoints->SetPoint(0, x, y, z); +} + +void CrossCursorAnnotationActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + // RebuildRenderPoint(); + vtkNew source; + source->SetPoints(renderPoints); + source->Update(); + renderData->DeepCopy(source->GetOutput()); +} + +void CrossCursorAnnotationActor::SetRenderer(vtkRenderer *ren) { + DraggableActor::SetRenderer(ren); +} + +void CrossCursorAnnotationActor::selfDragCb(vtkObject *, unsigned long, void *data) { + vtkPoints *pts = static_cast(data); + double *pos = pts->GetPoint(0); + +} + +void CrossCursorAnnotationActor::onMeasureMouseMove(vtkRenderWindowInteractor *iren) { +} + +void CrossCursorAnnotationActor::Transform(float x, float y) +{ + this->actor2D->SetDisplayPosition(x,y); + MapScreenPointToWorld(x,y, this->Renderer, WorldSlicePoint); +} + +bool CrossCursorAnnotationActor::onMeasureLeftButtonDown(vtkRenderWindowInteractor *iren) +{ + return true; +} + +bool CrossCursorAnnotationActor::onMeasureLeftButtonUp(vtkRenderWindowInteractor *iren) { + return false; +} diff --git a/src/src/Rendering/Measure/CrossCursorAnnotationActor.h b/src/src/Rendering/Measure/CrossCursorAnnotationActor.h new file mode 100644 index 0000000..e499c15 --- /dev/null +++ b/src/src/Rendering/Measure/CrossCursorAnnotationActor.h @@ -0,0 +1,60 @@ +#ifndef OMEGAV_CROSSCURSORANNOTATIONACTOR_H + +#define OMEGAV_CROSSCURSORANNOTATIONACTOR_H + + +#include "Rendering/Core/DraggableActor.h" +#include "Rendering/Measure/Measure.h" + + +class CrossCursorAnnotationActor : public DraggableActor, public Measure { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static CrossCursorAnnotationActor *New(); + + vtkTypeMacro(CrossCursorAnnotationActor, DraggableActor); + + //@} + void SetRenderer(vtkRenderer *ren) override; + + void BuildShape() override; + + void SetWorldPosition1(double x, double y, double z); + + void SetWorldPosition1(double *pos) { + SetWorldPosition1(pos[0], pos[1], pos[2]); + } + + void Transform(float x, float y) override; + bool onMeasureLeftButtonDown(vtkRenderWindowInteractor *) override; + + void onMeasureMouseMove(vtkRenderWindowInteractor *) override; + + bool onMeasureLeftButtonUp(vtkRenderWindowInteractor *) override; + + // NextMeasureMacro(LineAnnotationActor); + + void ForceDelete() override { + this->SetRenderer(nullptr); + DraggableActor::Delete(); + } + + vtkGetVector3Macro(WorldSlicePoint,vtkTypeFloat64); + +protected: + CrossCursorAnnotationActor(); + + + ~CrossCursorAnnotationActor() override; + + void selfDragCb(vtkObject *, unsigned long event, void *data); + + double WorldSlicePoint[3] = {.0,.0,.0}; + +}; + + +#endif //OMEGAV_CROSSCURSORANNOTATIONACTOR_H diff --git a/src/src/Rendering/Measure/EllipseAnnotationActor.cpp b/src/src/Rendering/Measure/EllipseAnnotationActor.cpp index 26bd26e..d7144d7 100644 --- a/src/src/Rendering/Measure/EllipseAnnotationActor.cpp +++ b/src/src/Rendering/Measure/EllipseAnnotationActor.cpp @@ -281,8 +281,8 @@ void EllipseAnnotationActor::drawCircle(double *p1, double *p2) { nIndex[0] = 1; nIndex[1] = 2; } - int CenterX = (p1[nIndex[0]] + p2[nIndex[0]] ) / 2.0; - int CenterY = (p1[nIndex[1]] + p2[nIndex[1]] ) / 2.0; + double CenterX = (p1[nIndex[0]] + p2[nIndex[0]] ) / 2.0; + double CenterY = (p1[nIndex[1]] + p2[nIndex[1]] ) / 2.0; double r1 = round(abs(p1[nIndex[0]] - p2[nIndex[0]]) / 2.0*100.0)/100; double r2 = round(abs(p1[nIndex[1]]- p2[nIndex[1]]) / 2.0*100.0)/100; diff --git a/src/src/Rendering/Measure/RoundAnnotationActor.cpp b/src/src/Rendering/Measure/RoundAnnotationActor.cpp index c86fdde..fbfb622 100644 --- a/src/src/Rendering/Measure/RoundAnnotationActor.cpp +++ b/src/src/Rendering/Measure/RoundAnnotationActor.cpp @@ -274,11 +274,11 @@ void RoundAnnotationActor::drawCircle(double *p1, double *p2) { double r2 = round(abs(p1[nIndex[1]]- p2[nIndex[1]]) / 2.0*100.0)/100; double r = r1r){ CenterX = CenterX > p2[nIndex[0]]?(r+p2[nIndex[0]]):(p2[nIndex[0]]-r); } - int CenterY = (p1[nIndex[1]] + p2[nIndex[1]] ) / 2.0; + double CenterY = (p1[nIndex[1]] + p2[nIndex[1]] ) / 2.0; if (abs(CenterY-p2[nIndex[1]])>r){ CenterY = CenterY > p2[nIndex[1]]?(r+p2[nIndex[1]]):(p2[nIndex[1]]-r); } diff --git a/src/src/Rendering/Source/vtkCrossCursorSource.cpp b/src/src/Rendering/Source/vtkCrossCursorSource.cpp new file mode 100644 index 0000000..463e2ce --- /dev/null +++ b/src/src/Rendering/Source/vtkCrossCursorSource.cpp @@ -0,0 +1,60 @@ + +#include "vtkCrossCursorSource.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" + +//---------------------------------------------------------------------------- +vtkStandardNewMacro(vtkCrossCursorSource) + +//---------------------------------------------------------------------------- +vtkCrossCursorSource::vtkCrossCursorSource() { + this->Closed = 0; +} + +//---------------------------------------------------------------------------- +vtkCrossCursorSource::~vtkCrossCursorSource() {} + +//---------------------------------------------------------------------------- +int vtkCrossCursorSource::RequestData(vtkInformation * vtkNotUsed(request), + vtkInformationVector ** vtkNotUsed(inputVector), vtkInformationVector *outputVector) { + // get the info object + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the output + vtkPolyData *output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkIdType numPoints = this->GetNumberOfPoints(); + // must have a mouse point point + if (numPoints < 1) return 1; + vtkNew pts; + vtkNew lines; + double centerPt[3] = {0.0, 0.0, 0.0}; + this->Points->GetPoint(0, centerPt); + vtkIdType pids[2]; + // pts->InsertNextPoint(centerPt[0]-10, centerPt[1], 0); + // pts->InsertNextPoint(centerPt[0]+10, centerPt[1], 0); + // lines->InsertNextCell(2, pids); + // pts->InsertNextPoint(centerPt[0], centerPt[1]-5, 0); + // pts->InsertNextPoint(centerPt[0], centerPt[1]+5, 0); + // lines->InsertNextCell(2, pids); + + + pids[0] = pts->InsertNextPoint(centerPt[0] - 3, centerPt[1], 0); + pids[1] = pts->InsertNextPoint(centerPt[0] - 20, centerPt[1], 0); + lines->InsertNextCell(2, pids); + pids[0] = pts->InsertNextPoint(centerPt[0] + 3, centerPt[1], 0); + pids[1] = pts->InsertNextPoint(centerPt[0] + 20, centerPt[1], 0); + lines->InsertNextCell(2, pids); + pids[0] = pts->InsertNextPoint(centerPt[0], centerPt[1] - 3, 0); + pids[1] = pts->InsertNextPoint(centerPt[0], centerPt[1] - 20, 0); + lines->InsertNextCell(2, pids); + pids[0] = pts->InsertNextPoint(centerPt[0], centerPt[1] + 3, 0); + pids[1] = pts->InsertNextPoint(centerPt[0], centerPt[1] + 20, 0); + lines->InsertNextCell(2, pids); + output->SetPoints(pts); + output->SetLines(lines); + return 1; +} + +void vtkCrossCursorSource::PrintSelf(ostream &os, vtkIndent indent) { + vtkPolyLineSource::PrintSelf(os, indent); +} diff --git a/src/src/Rendering/Source/vtkCrossCursorSource.h b/src/src/Rendering/Source/vtkCrossCursorSource.h new file mode 100644 index 0000000..061df9e --- /dev/null +++ b/src/src/Rendering/Source/vtkCrossCursorSource.h @@ -0,0 +1,32 @@ +#ifndef OMEGAV_VTKCROSSCURSORSOURCE_H +#define OMEGAV_VTKCROSSCURSORSOURCE_H + + +#include + +class vtkCrossCursorSource : public vtkPolyLineSource { +public: + /** + * Construct cone with angle of 45 degrees. + */ + static vtkCrossCursorSource *New(); + + vtkTypeMacro(vtkCrossCursorSource, vtkPolyDataAlgorithm); + + void PrintSelf(ostream &os, vtkIndent indent) override; +protected: + vtkCrossCursorSource(); + + ~vtkCrossCursorSource() override; + + int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *) override; + +private: + vtkCrossCursorSource(const vtkCrossCursorSource &) = delete; + + void operator=(const vtkCrossCursorSource &) = delete; + +}; + + +#endif /* OMEGAV_VTKCROSSCURSORSOURCE_H */ diff --git a/src/src/Rendering/Viewer/DICOMImageViewer.cxx b/src/src/Rendering/Viewer/DICOMImageViewer.cxx index e8e61b2..5b4e61e 100644 --- a/src/src/Rendering/Viewer/DICOMImageViewer.cxx +++ b/src/src/Rendering/Viewer/DICOMImageViewer.cxx @@ -313,7 +313,6 @@ void DICOMImageViewer::InstallPipeline() { if (!this->InteractorStyle) { this->InteractorStyle = ActorDraggableInteractorStyle::New(); this->InteractorStyle->SetInteractionModeToImageSlicing(); - this->InteractorStyle->SetCurrentImageNumber(0); this->InteractorStyle->SetCornerAnnotation(cornerAnnotation); this->InteractorStyle->AddObserver(DraggableStyleEvents::EndMeasureEvent, @@ -333,6 +332,12 @@ void DICOMImageViewer::InstallPipeline() { this->InteractorStyle->AddObserver(vtkCommand::EventIds::EndWindowLevelEvent, this, &DICOMImageViewer::raiseEvent); this->InteractorStyle->AddObserver(vtkCommand::EventIds::EndPanEvent, this, &DICOMImageViewer::raiseEvent); + + this->InteractorStyle->AddObserver(DraggableStyleEvents::StartSyncSlicePointEvent, this, &DICOMImageViewer::raiseEvent); + this->InteractorStyle->AddObserver(DraggableStyleEvents::SyncSlicePointEvent, this, &DICOMImageViewer::raiseEvent); + + this->InteractorStyle->AddObserver(DraggableStyleEvents::EndSyncSlicePointEvent, this, &DICOMImageViewer::raiseEvent); + } this->Interactor->SetInteractorStyle(this->InteractorStyle); @@ -467,7 +472,14 @@ void DICOMImageViewer::SetSlice(int slice) { } int lastSliceNumber = this->ImageMapper->GetSliceNumber(); + if (InteractorStyle){ + if (slice == 0){ + InteractorStyle->SetCurrentRenderer(this->Renderer); + InteractorStyle->SetCurrentImageNumber(0); + } + } if (lastSliceNumber == slice) { + return; } @@ -496,10 +508,6 @@ void DICOMImageViewer::SetSlice(int slice) { // this->Render(); UpdateTopLeftCornerInfo(); if (InteractorStyle){ - if (slice == 0){ - InteractorStyle->SetCurrentRenderer(this->Renderer); - InteractorStyle->SetCurrentImageNumber(0); - } double direction = (double)(slice - lastSliceNumber); double focusPoint[5] = {.0, .0, .0 }; GetSlicePoint(focusPoint); @@ -620,6 +628,33 @@ void DICOMImageViewer::SyncSliceWithFocus(double *point) UpdateTopLeftCornerInfo(); } +void DICOMImageViewer::SyncSlicePointSameDirection(double *point) { + if (point == nullptr){ + this->ActiveReferenceLine(); + this->InteractorStyle->AdjustSyncCrossPoint(nullptr); + this->Render(); + } + else{ + double focusPoint[4] = {point[0], point[1], point[2], 1.0}; + WorldToModelMatrix->MultiplyPoint(focusPoint, focusPoint); + double f[3] = {.0, .0, .0}; + Renderer->GetActiveCamera()->GetFocalPoint(f); + double bounds[6] = {.0, .0, .0, .0, .0, .0}; + ImageMapper->GetBounds(bounds); + double displayPoint [3] ={.0}; + Renderer->SetWorldPoint(focusPoint); + Renderer->WorldToDisplay(); + Renderer->GetDisplayPoint(displayPoint); + f[SliceIJK] = focusPoint[SliceIJK]; + Renderer->GetActiveCamera()->SetFocalPoint(f); + this->UnActiveReferenceLine(); + this->InteractorStyle->AdjustSyncCrossPoint(displayPoint); + this->Render(); + UpdateTopLeftCornerInfo(); + Slice = ImageMapper->GetSliceNumber(); + } +} + // zoom------------------------------------------------------------------------ void DICOMImageViewer::SetZoomScale(double scale) { if (Renderer) { diff --git a/src/src/Rendering/Viewer/DICOMImageViewer.h b/src/src/Rendering/Viewer/DICOMImageViewer.h index ce2ed00..c12e704 100644 --- a/src/src/Rendering/Viewer/DICOMImageViewer.h +++ b/src/src/Rendering/Viewer/DICOMImageViewer.h @@ -373,6 +373,8 @@ public: return &raiser; } + void SyncSlicePointSameDirection(double *point); + void SyncSliceWithFocus(double *point); diff --git a/src/src/UI/Manager/ImageViewManager.cpp b/src/src/UI/Manager/ImageViewManager.cpp index b0abbc5..d29a1a3 100644 --- a/src/src/UI/Manager/ImageViewManager.cpp +++ b/src/src/UI/Manager/ImageViewManager.cpp @@ -57,6 +57,8 @@ void ImageViewManager::add(DicomImageView *view) { connect(view, &DicomImageView::onSlice, this, &ImageViewManager::viewSliced); + connect(view, &DicomImageView::onSyncPointSlice, + this, &ImageViewManager::viewSyncPoint); connect(view, &DicomImageView::onEndPan, this, &ImageViewManager::viewPaned); connect(view, &DicomImageView::onEndZoom, @@ -294,6 +296,20 @@ void ImageViewManager::viewSliced(DicomImageView *src, void *sliceData) { renderReferenceLine(); } +void ImageViewManager::viewSyncPoint(DicomImageView *src, void * sliceData){ + //Sync slice Point + if ( src->getImageViewer()->GetInteractorStyle()->GetInteractionMode() == VTKIS_SYNCPOINT){ + this->smartDo(nullptr,[](auto v, auto callData) { + if (v->hasSeries()) { + double *r = (double *) callData; + v->syncSliceWithPoint(r); + v->SyncScrollBar(); + } + }, src, sliceData, ImageViewManager::SameSeries); + } +} + + void ImageViewManager::renderReferenceLine() { vtkPoints* pts = currentView->hasSeries() ? currentView->getSliceBoundPoints() : nullptr; smartDo([](auto v, auto callData) { diff --git a/src/src/UI/Manager/ImageViewManager.h b/src/src/UI/Manager/ImageViewManager.h index 663fc16..6594255 100644 --- a/src/src/UI/Manager/ImageViewManager.h +++ b/src/src/UI/Manager/ImageViewManager.h @@ -61,6 +61,8 @@ public: void viewDoubleClicked(DicomImageView *view); + void viewSyncPoint(DicomImageView *src, void * sliceData); + void viewSliced(DicomImageView *src, void *sliceData); void viewPaned(DicomImageView *src, void* offsetVector); diff --git a/src/src/UI/Widget/ImageView/dicomimageview.cpp b/src/src/UI/Widget/ImageView/dicomimageview.cpp index cb4e5a3..b61f8d7 100644 --- a/src/src/UI/Widget/ImageView/dicomimageview.cpp +++ b/src/src/UI/Widget/ImageView/dicomimageview.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include #include #include @@ -488,6 +490,29 @@ void DicomImageView::dispatchEvent(vtkObject *, unsigned long eid, void *callDat emit onSlice(this, callData); break; } + case (DraggableStyleEvents::StartSyncSlicePointEvent): { + this->setCursor(Qt::BlankCursor); + this->mImageViewer->Render(); + //invoke event + break; + } + case (DraggableStyleEvents::SyncSlicePointEvent): { + double * p = (double*)callData; + double focalPoint[3]={0}; + focalPoint[0] = p[0]; + focalPoint[1] = p[1]; + focalPoint[2] = p[2]; + mImageViewer->TransformPointM2W(focalPoint); + //invoke event + emit onSyncPointSlice(this, focalPoint); + break; + } + case (DraggableStyleEvents::EndSyncSlicePointEvent): { + this->setCursor(Qt::ArrowCursor); + //invoke event + emit onSyncPointSlice(this, nullptr); + break; + } default: break; } diff --git a/src/src/UI/Widget/ImageView/dicomimageview.h b/src/src/UI/Widget/ImageView/dicomimageview.h index 1b7598e..09fba9f 100644 --- a/src/src/UI/Widget/ImageView/dicomimageview.h +++ b/src/src/UI/Widget/ImageView/dicomimageview.h @@ -177,6 +177,10 @@ public: return this->mImageViewer->GetReconPlane() == view->mImageViewer->GetReconPlane(); } + void syncSliceWithPoint(double *point) { + mImageViewer->SyncSlicePointSameDirection(point); + } + void syncSliceFocus(double *point) { mImageViewer->SyncSliceWithFocus(point); } @@ -220,6 +224,8 @@ signals: void onSlice(DicomImageView *view, void *calldata); + void onSyncPointSlice(DicomImageView *view, void *calldata); + void onEndPan(DicomImageView *view, void * offsetVector); void onEndZoom(DicomImageView *view, double* scaleFactor); diff --git a/src/src/UI/Widget/ToolBar/DefaultToolBar.cpp b/src/src/UI/Widget/ToolBar/DefaultToolBar.cpp index 38b065e..cf58bf0 100644 --- a/src/src/UI/Widget/ToolBar/DefaultToolBar.cpp +++ b/src/src/UI/Widget/ToolBar/DefaultToolBar.cpp @@ -17,7 +17,7 @@ namespace { const char *SYNC_MANUAL_URL = ":/InfiniteViewer/Icon/sync/sync_manual.png"; const char *SYNC_AUTO_URL = ":/InfiniteViewer/Icon/sync/sync_auto.png"; const char *SYNC_DIS_URL = ":/InfiniteViewer/Icon/sync/sync_dis.png"; - const int ACTION_COUNT = 8; + const int ACTION_COUNT = 9; const ActionProperty MEASURE_ACTIIONS[ACTION_COUNT] = { { ":/InfiniteViewer/Icon/distance.png", AnnotationActorType::RulerAnn}, { ":/InfiniteViewer/Icon/angle.png", AnnotationActorType::AngleAnn}, @@ -26,7 +26,9 @@ namespace { { ":/InfiniteViewer/Icon/arrow.png", AnnotationActorType::ArrowAnn}, { ":/InfiniteViewer/Icon/ellipse.png", AnnotationActorType::EllipseAnn}, { ":/InfiniteViewer/Icon/diameter.png", AnnotationActorType::DiameterAnn}, - { ":/InfiniteViewer/Icon/text.png", AnnotationActorType::TextAnn} + { ":/InfiniteViewer/Icon/text.png", AnnotationActorType::TextAnn}, + { ":/InfiniteViewer/Icon/crosshair.png", AnnotationActorType::SyncPoint} + }; } @@ -413,9 +415,13 @@ void DefaultToolBar::initMeasureButton() { QMenu *m; m = new QMenu(this); - for (int j = 0; j < ACTION_COUNT; ++j) { + for (int j = 0; j < ACTION_COUNT-1; ++j) { ADD_MEASURE_ACTION(j); } + + m->addAction(tr("Location Point"), this,[=](){ + emit modeChanged(38); + }); m->addSeparator(); ADD_DEL_ACTION(tr("Delete selected"), AnnotationDeleteType::DeleteSelectedAnn); ADD_DEL_ACTION(tr("Delete all in current slice"), AnnotationDeleteType::DeleteSliceAnn); @@ -423,10 +429,7 @@ void DefaultToolBar::initMeasureButton() { mBtnMeasure->setPopupMode(QToolButton::MenuButtonPopup); mBtnMeasure->setMenu(m); - m->addSeparator(); - // m->addAction(tr("Localtion Point"), this,[=](){ - // emit modeChanged(38); - // }); + } void DefaultToolBar::initFusionButton() { diff --git a/src/translations/en_US.ts b/src/translations/en_US.ts index e136d33..7688141 100644 --- a/src/translations/en_US.ts +++ b/src/translations/en_US.ts @@ -261,6 +261,11 @@ Text + + + Location Point + + Custom window width and level @@ -297,112 +302,112 @@ - + Delete selected - + Delete all in current slice - + Delete all in current series - + Fusion - + Cine - + Delete current series - + Transformations - + Rotate 90 CCW - + Rotate 90 CW - + Rotate 180 - + Flip horizontal - + Flip vertical - + Clear transformations - + MPR - + 3D MPR - + Coronal - + Sagittal - + Axial - + Full screen - + Exit full screen mode - + Minimize - + Close @@ -410,12 +415,12 @@ DicomImageView - + Error - + Unable to read file %1 diff --git a/src/translations/zh_CN.ts b/src/translations/zh_CN.ts index 59df120..7938452 100644 --- a/src/translations/zh_CN.ts +++ b/src/translations/zh_CN.ts @@ -337,112 +337,117 @@ 测量工具 - + Delete selected 删除选中 - + Delete all in current slice 删除当前图像中所有测量 - + Delete all in current series 删除当前序列所有图像 - + + Location Point + 病灶定位 + + + Fusion - + Cine - + Delete current series 删除当前序列所有测量 - + Transformations 翻页&旋转 - + Rotate 90 CCW 逆时针旋转90度 - + Rotate 90 CW 顺时针旋转90度 - + Rotate 180 旋转180度 - + Flip horizontal 水平翻转 - + Flip vertical 垂直翻转 - + Clear transformations 清除变换 - + MPR 多平面重建 - + 3D MPR - + Coronal 冠状面 - + Sagittal 矢状面 - + Axial 横断面 - + Full screen 全屏 - + Exit full screen mode 退出全屏 - + Minimize 最小化 - + Close 关闭 @@ -450,12 +455,12 @@ DicomImageView - + Error 错误 - + Unable to read file %1 无法读取文件 %1