From 27857d4263000ca49cedb1b0310605f400262c43 Mon Sep 17 00:00:00 2001 From: kradchen Date: Mon, 30 Jun 2025 13:19:28 +0800 Subject: [PATCH] feat: add syncpoint function --- CMakeLists.txt | 2 +- src/Icon/crosshair.png | Bin 0 -> 3403 bytes src/QDicomViewer.qrc | 1 + src/src/Common/QGlobals.h | 1 + .../ActorDraggableInteractorStyle.cpp | 83 ++++++++++++++++++ .../ActorDraggableInteractorStyle.h | 7 ++ src/src/Rendering/Core/RenderingDefines.h | 6 +- .../Measure/CrossCursorAnnotationActor.cpp | 72 +++++++++++++++ .../Measure/CrossCursorAnnotationActor.h | 60 +++++++++++++ .../Measure/EllipseAnnotationActor.cpp | 4 +- .../Measure/RoundAnnotationActor.cpp | 4 +- .../Rendering/Source/vtkCrossCursorSource.cpp | 60 +++++++++++++ .../Rendering/Source/vtkCrossCursorSource.h | 32 +++++++ src/src/Rendering/Viewer/DICOMImageViewer.cxx | 45 ++++++++-- src/src/Rendering/Viewer/DICOMImageViewer.h | 2 + src/src/UI/Manager/ImageViewManager.cpp | 16 ++++ src/src/UI/Manager/ImageViewManager.h | 2 + .../UI/Widget/ImageView/dicomimageview.cpp | 25 ++++++ src/src/UI/Widget/ImageView/dicomimageview.h | 6 ++ src/src/UI/Widget/ToolBar/DefaultToolBar.cpp | 17 ++-- src/translations/en_US.ts | 53 ++++++----- src/translations/zh_CN.ts | 53 ++++++----- 22 files changed, 485 insertions(+), 66 deletions(-) create mode 100644 src/Icon/crosshair.png create mode 100644 src/src/Rendering/Measure/CrossCursorAnnotationActor.cpp create mode 100644 src/src/Rendering/Measure/CrossCursorAnnotationActor.h create mode 100644 src/src/Rendering/Source/vtkCrossCursorSource.cpp create mode 100644 src/src/Rendering/Source/vtkCrossCursorSource.h 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 0000000000000000000000000000000000000000..4ff4a7009b00a741c0ef96cd9be7e78240d84cb5 GIT binary patch literal 3403 zcmeHKSx}S777j5$P=o-3!~sL14vIP~0wS`-=*c?tQxtw`$&e=*d%)%V|y{id-{pEZ}yN!{H^eB}#TH`TxJ zlXZu?n#LvAeLh{G!m;RMa9*jV?nj7fV;4PU_j%ve6-IQ7GA(xI1Vbo?!%j-M8xaR$-V*Ly?L0%?>7U&aacYPbK75g72OY^`GMQ~AUZ)}eCw#+2N*fuaKxrdsaUG{uZP38gV4E_loIO{y3o`yaS;)?0fN-1xH0&63_K#}M6z4~(5J|KlOc!~Xb(~w(t zwC>{(0G2g&_=y?8B3XE|xj!3n4;}dr{EG#mTw75tB$dZ*iL{!joe^~USGn~aARrTw zaxvAyZ!z`p2Jpe+$!M==k-u?&*(AAI%?4)vwn!aoU!KYnT7Iazo~9Y+cfXAcN@|7W zM-@^8Csbm;C^l&Nra1@o=vZrlp~p4Tg~bHN6Mx7Sr{Q?YakBme7^Q=($=- zDIsq9PMsG(wyjVPABF;wXECeS4s*n%gZ~2n|eZB0hcZ zQmyfVj80o2dMe=0$%rcirbsl^0#ye$udMurv{ar+oVfuO%G0p+8u!mhs~s1r^|-5L z`EvnLK#^MNV(FM{%1&zyh+YtV@a;)x1FtB4?A>LXE?h`M^(|r1rSdVlevhIusy<<& zBhQiQ8TQ`RG2}RH5P^Im!V3 zD~^+}scpP~wU@_?;c}GvMj;4=?6cSOPVwfiE&+A(sIeB-p0g*jKZP?*G92!T9&U_Z z@76L48*6Bc4LhWEcpaCJw1LAkLg+`p+FUrNBp{!{P|ut)d7@U?{tnI&y)u8|bekT& z6)&=2yF!WX!*@4~lIPSvO(=}fw@tK^I7NWP*5Uou9T84ED>dKxypU+%@y{(*^FkPd z?;$N)*}2H^kgsks1*xxR;JzX}Zl5L@dxYbib(Iy^ICI}H8^6B1mUBTzk^-}uP}f=X zfJ9Mqk;*ABLP`e1hA(~fc4ST?$IrfSWAU){nZQN@$C!_v$_Gt)DxiIp zoZJML)kM-@1ysZGZ4l9b8-s7VHNHnpMK=Q>BnFo>j2~n!Dyv9y{NNsDFFA*X1Lu=S zzsg+D2mf0P?HM}Hr26ZM1e3jDdUJM9ozKwddYqswRhK!1dA8v(S1-Fmuo7minwi@K z&z;4s)_bzY4<^rbOj=#izn#)9&gDS-xNFJzx7KZ9=a~CPzHvm zvud?9#I@RuxXR^iU&D~ER(hOm?ai*QBFxyd2a&b4Z94NsSo{iL1HpP*JkMz`g)1s) zS;WlAPM7|KPqL<6cuA3fs2-@~@?2dGXnTvj{^3rc?5ffy)hPw!IZN}LT{a-wJwA`v zO}79cZci73VK!vh-Wz%q3{cWX)>AeJ$at9(G>4KyiO8j|{(2x8h)1HM_IHi^z|agr2O=&vz(D2QuI@J6$O2338DfBYBbj(%Hv<1?2}q8EEgM$4x3_C2(V%d-3| z2Pkr)8*X1l^ML%_Hw`%nJ&`L9?Ve8OD0!I6Kq`z%t^m4IMDiSB)gR^7RN%9F6@gwr z@me6&UN=}Rvw+`GXUJWxiVB`*p%Rg-5%-3c8^_rvlS5sh*Mj-dKPz3J*KYHrK=pn< j61p0-^!bN2$dIcon/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