From 46d22eacd75b7cef7ce50657dff5e35cbb029e10 Mon Sep 17 00:00:00 2001 From: Krad Date: Tue, 13 Dec 2022 09:46:02 +0800 Subject: [PATCH] Add ResliceImageViewer, ResliceImageInteractorStyle, ResliceCursorLegendActor, MPRResliceWindow. --- .../ResliceImageInteractorStyle.cpp | 45 ++++ .../Interaction/ResliceImageInteractorStyle.h | 39 +++ .../Legend/ResliceCursorLegendActor.cpp | 233 ++++++++++++++++++ .../Legend/ResliceCursorLegendActor.h | 98 ++++++++ .../Rendering/Viewer/ResliceImageViewer.cpp | 233 ++++++++++++++++++ src/src/Rendering/Viewer/ResliceImageViewer.h | 73 ++++++ src/src/UI/Window/MPRResliceWindow.cpp | 219 ++++++++++++++++ src/src/UI/Window/MPRResliceWindow.h | 49 ++++ 8 files changed, 989 insertions(+) create mode 100644 src/src/Interaction/ResliceImageInteractorStyle.cpp create mode 100644 src/src/Interaction/ResliceImageInteractorStyle.h create mode 100644 src/src/Rendering/Legend/ResliceCursorLegendActor.cpp create mode 100644 src/src/Rendering/Legend/ResliceCursorLegendActor.h create mode 100644 src/src/Rendering/Viewer/ResliceImageViewer.cpp create mode 100644 src/src/Rendering/Viewer/ResliceImageViewer.h create mode 100644 src/src/UI/Window/MPRResliceWindow.cpp create mode 100644 src/src/UI/Window/MPRResliceWindow.h diff --git a/src/src/Interaction/ResliceImageInteractorStyle.cpp b/src/src/Interaction/ResliceImageInteractorStyle.cpp new file mode 100644 index 0000000..676ae09 --- /dev/null +++ b/src/src/Interaction/ResliceImageInteractorStyle.cpp @@ -0,0 +1,45 @@ +// +// Created by Krad on 2022/12/6. +// + +#include "ResliceImageInteractorStyle.h" + +#include +#include +#include +#include +#include +#include +#include + +vtkStandardNewMacro(ResliceImageInteractorStyle) + +ResliceImageInteractorStyle::ResliceImageInteractorStyle():vtkInteractorStyleImage() { + +} + +ResliceImageInteractorStyle::~ResliceImageInteractorStyle() { + +} + +void ResliceImageInteractorStyle::OnMouseMove() { + + if (triggerEvent) + { + this->InvokeEvent(vtkCommand::UserEvent+20,Interactor->GetEventPosition()); + this->Interactor->Render(); + } + else{ + vtkInteractorStyleImage::OnMouseMove(); + } +} + +void ResliceImageInteractorStyle::OnLeftButtonDown() { + triggerEvent = false; + vtkInteractorStyleImage::OnLeftButtonDown(); +} + +void ResliceImageInteractorStyle::OnLeftButtonUp() { + vtkInteractorStyleImage::OnLeftButtonUp(); + triggerEvent = true; +} diff --git a/src/src/Interaction/ResliceImageInteractorStyle.h b/src/src/Interaction/ResliceImageInteractorStyle.h new file mode 100644 index 0000000..143120a --- /dev/null +++ b/src/src/Interaction/ResliceImageInteractorStyle.h @@ -0,0 +1,39 @@ +// +// Created by Krad on 2022/12/6. +// + +#ifndef RENDERLAB_RESLICEIMAGEINTERACTORSTYLE_H +#define RENDERLAB_RESLICEIMAGEINTERACTORSTYLE_H + +#include + +#include +#include + +class ResliceImageInteractorStyle:public vtkInteractorStyleImage { +public: + static ResliceImageInteractorStyle *New(); + + vtkTypeMacro(ResliceImageInteractorStyle, vtkInteractorStyleImage); + + void OnLeftButtonDown() override; + + void OnLeftButtonUp() override; + + void OnMouseMove() override; +protected: + + ResliceImageInteractorStyle(); + + ~ResliceImageInteractorStyle() override; + +private: + ResliceImageInteractorStyle(const ResliceImageInteractorStyle &) = delete; + + void operator=(const ResliceImageInteractorStyle &) = delete; + + bool triggerEvent = true; +}; + + +#endif //RENDERLAB_RESLICEIMAGEINTERACTORSTYLE_H diff --git a/src/src/Rendering/Legend/ResliceCursorLegendActor.cpp b/src/src/Rendering/Legend/ResliceCursorLegendActor.cpp new file mode 100644 index 0000000..0d81cf4 --- /dev/null +++ b/src/src/Rendering/Legend/ResliceCursorLegendActor.cpp @@ -0,0 +1,233 @@ +// +// Created by Krad on 2022/12/1. +// + +#include "ResliceCursorLegendActor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Rendering/Core/ControlPointRActor.h" + +namespace { + class Vector2 { + public: + Vector2(double x, double y) { + X = x; + Y = y; + } + + double X; + double Y; + + double getValue(int index){ + if (index == 0) return X; + else return Y; + } + + Vector2& operator=(const Vector2& other){ + this->X = other.X; + this->Y = other.Y; + return *this; + } + Vector2 operator-(const Vector2 &other) const { + return {this->X - other.X, this->Y - other.Y}; + } + + Vector2 operator+(const Vector2 &other) const { + return {this->X + other.X, this->Y + other.Y}; + } + + Vector2 operator*(double val) const { + return {this->X * val, this->Y * val}; + } + + Vector2 operator/(const double val) const { + return {this->X / val, this->Y / val}; + } + + static double cross(const Vector2 &a, const Vector2 &b) { + return a.X * b.Y - a.Y * b.X; + } + }; + bool IntersectLine2D(const Vector2& p1,const Vector2& v1, + const Vector2& p2, const Vector2& v2, Vector2& intersectPoint){ + double crossV = Vector2::cross(v1, v2); + if (abs(crossV) > 1e-6) { + double t = Vector2::cross(p2-p1, v2) / crossV; + intersectPoint = p1 + v1 * t; + return true; + } + return false; + }; +} + +vtkStandardNewMacro(ResliceCursorLegendActor) + + +ResliceCursorLegendActor::ResliceCursorLegendActor() +: vtkProp() +, LineActor(vtkActor2D::New()) +, ControlPoint(ControlPointRActor::New()) +, LineShadow(vtkActor2D::New()) +, senseArea(vtkActor2D::New()) +, linePolyData(vtkPolyData::New()){ + vtkNew pts; + pts->SetNumberOfPoints(2); + pts->SetPoint(0,0,0,0); + pts->SetPoint(1,0,0,0); + linePolyData->SetPoints(pts); + vtkIdType ids[2] = {0, 1}; + linePolyData->GetLines()->InsertNextCell(2,ids); + vtkNew mapper ; + vtkNew mapper1 ; + vtkNew mapper2 ; + mapper->SetInputData(linePolyData); + mapper1->SetInputData(linePolyData); + mapper2->SetInputData(linePolyData); + LineActor->SetMapper(mapper); + LineShadow->SetMapper(mapper1); + senseArea->SetMapper(mapper2); + ControlPoint->SetWorldPosition(0,0,0); +} + +ResliceCursorLegendActor::~ResliceCursorLegendActor() { + +} + +// 避免坐标系值问题导致线变粗 +void AlignDoubleVector3(double* in, double *out){ + for (int i = 0; i < 3; ++i) { + out[i] = ((int)in[i]) + 0.5; + } +} + +void ResliceCursorLegendActor::BuildShape(vtkRenderer *renderer) { + //handle disk + if (HandleUpdated){ + renderer->SetDisplayPoint(Handle2DPoint[0],Handle2DPoint[1], 0); + renderer->DisplayToWorld(); + renderer->GetWorldPoint(HandlePoint); + HandleUpdated = false; + } + ControlPoint->SetWorldPosition(HandlePoint); + + //cross hair + double vector[3] = {.0, .0, .0}; + vtkMath::Cross(GetSliceDirectionVector(), GetProjectDirectionVector(), vector); + vtkMath::Normalize(vector); + double point1[4] = {SlicePoint[0] , + SlicePoint[1] , + SlicePoint[2], + 1.0}; + + double point2[4] = {SlicePoint[0] + vector[0], + SlicePoint[1] + vector[1], + SlicePoint[2] + vector[2], + 1.0}; + double out1[4] = {.0, .0, .0,.0}; + double out2[4] = {.0, .0, .0, .0}; + linePolyData->GetPoints()->SetNumberOfPoints(2); + renderer->SetWorldPoint(point1); + renderer->WorldToDisplay(); + renderer->GetDisplayPoint(out1); + + + renderer->SetWorldPoint(point2); + renderer->WorldToDisplay(); + renderer->GetDisplayPoint(out2); + + double vector2D[2] = {out2[0] - out1[0], out2[1] - out1[1]}; + vtkMath::Normalize2D(vector2D); + Vector2 v1 = {vector2D[0], vector2D[1]}; + Vector2 screenVectors[2] ={{0, 1}, {1, 0}}; + Vector2 p0 = {out1[0],out1[1]}; + int *size = renderer->GetSize(); + Vector2 pts[2] = {{-0.5,-0.5} ,{size[0] + 0.5,size[1] + 0.5}}; + Vector2 intersectPoint(.0, .0); + // calc the point, which line cross the screen border + int pointIdx = 0; + // line vector loop + for (int i = 0; i < 2; ++i) { + // line point loop + for (const auto & pt : pts) { + if (pointIdx >= 2)return; + if (IntersectLine2D(p0, v1, pt, screenVectors[i], intersectPoint)) { + int pcV = (int) intersectPoint.getValue(i); + if (pcV >= -1 && pcV <= size[i] + 1) { + linePolyData->GetPoints()->SetPoint(pointIdx++, (int)intersectPoint.X + 0.5, (int)intersectPoint.Y + 0.5, 0); + } + } + } + } +} + +void ResliceCursorLegendActor::ReleaseGraphicsResources(vtkWindow * window) { + senseArea->ReleaseGraphicsResources(window); + LineActor->ReleaseGraphicsResources(window); + LineShadow->ReleaseGraphicsResources(window); + ControlPoint->ReleaseGraphicsResources(window); + vtkProp::ReleaseGraphicsResources(window); +} + +int ResliceCursorLegendActor::RenderOverlay(vtkViewport *viewport) { + auto renderer = vtkRenderer::SafeDownCast(viewport); + if (!renderer) return 0; + if (firstRender){ + ControlPoint->SetRenderer(renderer); + firstRender = false; + } + BuildShape(renderer); + if (senseArea->GetVisibility())senseArea->RenderOverlay(viewport); + if (LineShadow->GetVisibility())LineShadow->RenderOverlay(viewport); + if (LineActor->GetVisibility())LineActor->RenderOverlay(viewport); + if (ControlPoint->GetVisibility())ControlPoint->RenderOverlay(viewport); + return 1; +} + +vtkProperty2D *ResliceCursorLegendActor::GetProperty() { + return LineActor->GetProperty(); +} + +void ResliceCursorLegendActor::UpdateMousePosition(int* pos) { + double point0[3] = {.0,.0,.0}; + double point1[3] = {.0,.0,.0}; + linePolyData->GetPoints()->GetPoint(0,point0); + linePolyData->GetPoints()->GetPoint(1,point1); + double vector[3] = { + point1[0] - point0[0], + point1[1] - point0[1], + 0 + }; + vtkMath::Normalize(vector); + double zVector[3] = {.0, .0, 1.0}; + double out[3] = {.0, .0, .0}; + vtkMath::Cross(vector, zVector, out); + Vector2 p1 (pos[0]+0.5,pos[1]+0.5); + Vector2 p2 (point0[0],point0[1]); + Vector2 v1 (out[0],out[1]); + Vector2 v2 (vector[0],vector[1]); + Vector2 intersectPoint (.0,.0); + if(IntersectLine2D(p1,v1,p2,v2,intersectPoint)) + { + Handle2DPoint[0] = (int)intersectPoint.X+0.5; + Handle2DPoint[1] = (int)intersectPoint.Y+0.5; + HandleUpdated = true; + this->Modified(); + } +} + +void ResliceCursorLegendActor::Pick() { + vtkProp::Pick(); +} diff --git a/src/src/Rendering/Legend/ResliceCursorLegendActor.h b/src/src/Rendering/Legend/ResliceCursorLegendActor.h new file mode 100644 index 0000000..1f44ca0 --- /dev/null +++ b/src/src/Rendering/Legend/ResliceCursorLegendActor.h @@ -0,0 +1,98 @@ +// +// Created by Krad on 2022/12/1. +// + +#ifndef RENDERLAB_RESLICECURSORLEGENDACTOR_H +#define RENDERLAB_RESLICECURSORLEGENDACTOR_H + +#include + +class vtkRenderer; +class vtkPolyData; +class vtkActor2D; +class vtkDiskSource; +class vtkProperty2D; +class ControlPointRActor; + +class ResliceCursorLegendActor:public vtkProp { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static ResliceCursorLegendActor *New(); + + vtkTypeMacro(ResliceCursorLegendActor, vtkProp); + + virtual void BuildShape(vtkRenderer *renderer); + + vtkSetVector3Macro(SlicePoint,double); + vtkGetVector3Macro(SlicePoint,double); + + vtkSetVector3Macro(SliceDirectionVector,double); + vtkGetVector3Macro(SliceDirectionVector,double); + + vtkSetVector3Macro(ProjectDirectionVector,double); + vtkGetVector3Macro(ProjectDirectionVector,double); + + vtkProperty2D* GetProperty(); + + //@{ + /** + * Methods to make this class behave as a vtkProp. + */ + double *GetBounds() VTK_SIZEHINT(6) override { return nullptr; } + + void GetActors(vtkPropCollection *) override {} + + void GetVolumes(vtkPropCollection *) override {} + + void ShallowCopy(vtkProp *prop) override {}; + + void ReleaseGraphicsResources(vtkWindow *) override; + + /** + * Method use to make this actor render in 2D scene; + * @param viewport + * @return render is success + */ + int RenderOverlay(vtkViewport *viewport) override; + + int RenderOpaqueGeometry(vtkViewport *vtkNotUsed(viewport)) override { return 0; } + + int RenderTranslucentPolygonalGeometry(vtkViewport *vtkNotUsed(viewport)) override { return 0; } + + int RenderVolumetricGeometry(vtkViewport *vtkNotUsed(viewport)) override { return 0; } + + vtkTypeBool HasTranslucentPolygonalGeometry() override { return 0; } + + void UpdateMousePosition(int* pos); + + void Pick() override; + +protected: + ResliceCursorLegendActor(); + + ~ResliceCursorLegendActor() override; +private: + ResliceCursorLegendActor(const ResliceCursorLegendActor &) = delete; + + void operator=(const ResliceCursorLegendActor &) = delete; + + vtkActor2D *LineActor; + ControlPointRActor *ControlPoint; + vtkActor2D *LineShadow; + vtkActor2D *senseArea; + vtkPolyData* linePolyData; + double LineHalfLength = 500.0; + double SlicePoint[3] = {.0, .0, .0}; + double HandlePoint[4] = {.0, .0, .0, .0}; + bool HandleUpdated = false; + bool firstRender = true; + double Handle2DPoint[2] = {.0, .0, }; + double SliceDirectionVector[3] = {.0, .0, .0}; + double ProjectDirectionVector[3] = {.0, .0, .0}; +}; + + +#endif //RENDERLAB_RESLICECURSORLEGENDACTOR_H diff --git a/src/src/Rendering/Viewer/ResliceImageViewer.cpp b/src/src/Rendering/Viewer/ResliceImageViewer.cpp new file mode 100644 index 0000000..89a6799 --- /dev/null +++ b/src/src/Rendering/Viewer/ResliceImageViewer.cpp @@ -0,0 +1,233 @@ +// +// Created by Krad on 2022/12/8. +// + +#include "ResliceImageViewer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Rendering/Legend/ResliceCursorLegendActor.h" +#include "Interaction/ResliceImageInteractorStyle.h" + +vtkStandardNewMacro(ResliceImageViewer); + +ResliceImageViewer::ResliceImageViewer() + : vtkObject() + , RenderWindow(nullptr) + , Renderer(vtkRenderer::New()) + , InteractorStyle(ResliceImageInteractorStyle::New()) + , Interactor(nullptr) + , Mapper(vtkImageResliceMapper::New()) + , cursor1(ResliceCursorLegendActor::New()) + , cursor2(ResliceCursorLegendActor::New()) + , Actor(vtkImageSlice::New()) + , OrientationMatrix(vtkMatrix4x4::New()) + , DefaultOrientation(0) + , FirstRender(true) + +{ + Mapper->SetSliceAtFocalPoint(true); + Mapper->SetSliceFacesCamera(true); + Mapper->SetResampleToScreenPixels(0); + Mapper->SetSeparateWindowLevelOperation(0); + Actor->SetMapper(Mapper); + + OrientationMatrix->Identity(); +} + +ResliceImageViewer::~ResliceImageViewer() { + if (this->OrientationMatrix) { + this->OrientationMatrix->Delete(); + this->OrientationMatrix = nullptr; + } + if (this->Mapper) { + vtkImageData* imageData_nullptr = nullptr; + this->Mapper->SetInputData(imageData_nullptr); + this->Mapper->Delete(); + this->Mapper = nullptr; + } + if (this->Actor) { + this->Actor->Delete(); + this->Actor = nullptr; + } + + if (this->Renderer) { + this->Renderer->Delete(); + this->Renderer = nullptr; + } + + if (this->RenderWindow) { + this->RenderWindow->Delete(); + this->RenderWindow = nullptr; + } + + if (this->Interactor) { + this->Interactor->Delete(); + this->Interactor = nullptr; + } + + if (this->InteractorStyle) { + this->InteractorStyle->Delete(); + this->InteractorStyle = nullptr; + } +} + +void ResliceImageViewer::InstallPipeline() { + if (this->RenderWindow && this->Renderer) { + this->Renderer->GetActiveCamera()->ParallelProjectionOn(); + this->Renderer->SetUseFXAA(1); + this->RenderWindow->AddRenderer(this->Renderer); + this->Interactor = this->RenderWindow->GetInteractor(); + InteractorStyle->HandleObserversOn(); + this->Interactor->SetInteractorStyle(InteractorStyle); + } + + + if (this->Renderer && this->Actor) { + this->Renderer->AddViewProp(Actor); + this->Renderer->SetBackground(0., 0., 0.); + } +} + +void ResliceImageViewer::UnInstallPipeline() { + if (this->Renderer && this->Actor) { + this->Renderer->RemoveViewProp(this->Actor); + } + + if (this->RenderWindow && this->Renderer) { + this->RenderWindow->RemoveRenderer(this->Renderer); + } + if (this->Renderer && Actor) { + if (this->Renderer->HasViewProp(Actor)) + { + this->Renderer->RemoveViewProp(Actor); + } + Renderer->RemoveAllObservers(); + } + if (this->Interactor) { + this->Interactor->SetInteractorStyle(nullptr); + this->Interactor->SetRenderWindow(nullptr); + } +} + +void ResliceImageViewer::SetRenderWindow(vtkRenderWindow *renderWindow) { + if(renderWindow == RenderWindow)return; + if(RenderWindow) { + UnInstallPipeline(); + RenderWindow->Delete(); + } + RenderWindow = renderWindow; + InstallPipeline(); +} + +void ResliceImageViewer::SetInputData(vtkImageData *in) { + if (in && in != this->Mapper->GetInput()){ + in->Modified(); + this->Mapper->SetInputData(in); + } +} + +void ResliceImageViewer::Render() { + if (RenderWindow && Interactor){ + if (FirstRender){ + FirstRender = false; + Renderer->AddActor2D(cursor1); + Renderer->AddActor2D(cursor2); + cursor1->SetSlicePoint(Renderer->GetActiveCamera()->GetFocalPoint()); + cursor2->SetSlicePoint(Renderer->GetActiveCamera()->GetFocalPoint()); + double project[3] = {0,0.0,1.0}; + project[DefaultOrientation] = 1.; + OrientationMatrix->MultiplyPoint(project,project); + cursor1->SetProjectDirectionVector(project); + cursor2->SetProjectDirectionVector(project); + double sliceDirection1[4]{.0, .0, .0, 1.}; + double sliceDirection2[4]{.0, .0, .0, 1.}; + switch (DefaultOrientation) { + case 0:{ + sliceDirection1[2] = 1.0; + cursor1->GetProperty()->SetColor(0,0,1); + sliceDirection2[1] = 1.0; + cursor2->GetProperty()->SetColor(0,1,0); + break; + } + case 1:{ + sliceDirection1[0] = 1.0; + cursor1->GetProperty()->SetColor(1,0,0); + sliceDirection2[2] = 1.0; + cursor2->GetProperty()->SetColor(0,0,1); + break; + } + case 2:{ + sliceDirection1[0] = 1.0; + cursor1->GetProperty()->SetColor(1,0,0); + sliceDirection2[01] = 1.0; + cursor2->GetProperty()->SetColor(0,1,0); + break; + } + } + OrientationMatrix->MultiplyPoint(sliceDirection1,sliceDirection1); + OrientationMatrix->MultiplyPoint(sliceDirection2,sliceDirection2); + cursor1->SetSliceDirectionVector(sliceDirection1); + cursor2->SetSliceDirectionVector(sliceDirection2); + this->InteractorStyle->AddObserver(vtkCommand::UserEvent+20,this,&ResliceImageViewer::updateHandle); + //some first render logic + Render(); + return; + } + Interactor->Render(); + } +} + +void ResliceImageViewer::SetDefaultSliceOrientation(int orientation) { + double position[4] = {.0, .0, .0, 1.}; + double ViewUp[4] = {.0, .0, .0, 1.}; + switch (orientation){ + case 1:{ + position[1] = -1.0; + ViewUp[2] = 1.0; + break; + } + case 0:{ + position[0] = 1.0; + ViewUp[2] = 1.0; + break; + } + case 2: { + position[2] = -1.0; + ViewUp[1] = -1.0; + break; + } + default: + break; + } + DefaultOrientation = orientation; + OrientationMatrix->MultiplyPoint(position,position); + OrientationMatrix->MultiplyPoint(ViewUp,ViewUp); + vtkCamera* camera = Renderer->GetActiveCamera(); + camera->SetFocalPoint(.0, .0, .0); + camera->SetPosition(position); + camera->SetViewUp(ViewUp); + Renderer->ResetCamera(); +} + +void ResliceImageViewer::SetCoordsTransformMatrix(vtkMatrix4x4 *matrix4X4) { + OrientationMatrix->DeepCopy(matrix4X4); + + OrientationMatrix->Invert(); +} + +void ResliceImageViewer::updateHandle(vtkObject* sender, unsigned long eventID, void* callData) { + int* pos = (int*)callData; + cursor1->UpdateMousePosition(pos); + cursor2->UpdateMousePosition(pos); +} diff --git a/src/src/Rendering/Viewer/ResliceImageViewer.h b/src/src/Rendering/Viewer/ResliceImageViewer.h new file mode 100644 index 0000000..0b7c7f2 --- /dev/null +++ b/src/src/Rendering/Viewer/ResliceImageViewer.h @@ -0,0 +1,73 @@ +// +// Created by Krad on 2022/12/8. +// + +#ifndef OMEGAV_RESLICEIMAGEVIEWER_H +#define OMEGAV_RESLICEIMAGEVIEWER_H + +#include "vtkObject.h" + +class vtkImageData; + +class vtkRenderWindow; + +class vtkRenderer; + +class vtkRenderWindowInteractor; + +class ResliceImageInteractorStyle; + +class vtkImageResliceMapper; + +class vtkImageSlice; + +class vtkMatrix4x4; + +class ResliceCursorLegendActor; + +class ResliceImageViewer :public vtkObject { +public: + static ResliceImageViewer *New(); + + vtkTypeMacro(ResliceImageViewer, vtkObject); + + virtual void Render(); + + void SetRenderWindow(vtkRenderWindow* renderWindow); + vtkGetMacro(RenderWindow, vtkRenderWindow*) + + void SetInputData(vtkImageData *in); + + void SetDefaultSliceOrientation(int orientation); + void SetCoordsTransformMatrix(vtkMatrix4x4* matrix4X4); + +protected: + ResliceImageViewer(); + + ~ResliceImageViewer() override; + + virtual void InstallPipeline(); + + virtual void UnInstallPipeline(); + + void updateHandle(vtkObject* sender, unsigned long eventID, void* callData); +private: + ResliceImageViewer(const ResliceImageViewer &) = delete; + void operator=(const ResliceImageViewer &) = delete; + + vtkRenderWindow *RenderWindow; + vtkRenderer *Renderer; + vtkRenderer *MeasureRenderer; + ResliceImageInteractorStyle *InteractorStyle; + vtkRenderWindowInteractor *Interactor; + vtkImageResliceMapper* Mapper; + ResliceCursorLegendActor* cursor1; + ResliceCursorLegendActor* cursor2; + vtkImageSlice* Actor; + vtkMatrix4x4* OrientationMatrix; + int DefaultOrientation; + bool FirstRender; +}; + + +#endif //OMEGAV_RESLICEIMAGEVIEWER_H diff --git a/src/src/UI/Window/MPRResliceWindow.cpp b/src/src/UI/Window/MPRResliceWindow.cpp new file mode 100644 index 0000000..e0a02b7 --- /dev/null +++ b/src/src/UI/Window/MPRResliceWindow.cpp @@ -0,0 +1,219 @@ +// +// Created by Krad on 2022/12/8. +// + +#include "MPRResliceWindow.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "Common/SeriesImageSet.h" +#include "UI/Widget/ToolBar/ResliceMPRToolBar.h" +#include "Rendering/Viewer/ResliceImageViewer.h" + +MPRResliceWindow::MPRResliceWindow(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { + setObjectName("MPRWin"); + + this->setMinimumSize(800,600); + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setMargin(1); + layout->setSpacing(0); + auto toolBar = new ResliceMPRToolBar(this); + layout->addWidget(toolBar); + + mWidgetAxial = new QVTKOpenGLNativeWidget(this); + mWidgetAxial->setEnableHiDPI(true); + mWidgetAxial->setBaseSize(400,300); + mWidgetAxial->setSizePolicy(QSizePolicy::Policy::Expanding,QSizePolicy::Policy::Expanding); + mWidgetSagittal = new QVTKOpenGLNativeWidget(this); + mWidgetSagittal->setEnableHiDPI(true); + mWidgetSagittal->setBaseSize(400,300); + mWidgetSagittal->setSizePolicy(QSizePolicy::Policy::Expanding,QSizePolicy::Policy::Expanding); + + mWidgetCoronal = new QVTKOpenGLNativeWidget(this); + mWidgetCoronal->setEnableHiDPI(true); + mWidgetCoronal->setBaseSize(400,300); + mWidgetCoronal->setSizePolicy(QSizePolicy::Policy::Expanding,QSizePolicy::Policy::Expanding); + + setRenderWindowLayout(Columns_1_2); + + auto window1 = vtkGenericOpenGLRenderWindow::New(); + window1->SetLineSmoothing(1); + window1->SetPointSmoothing(1); + window1->SetPolygonSmoothing(1); + mViewerC = ResliceImageViewer::New(); + mWidgetCoronal->setRenderWindow(window1); + + auto window2 = vtkGenericOpenGLRenderWindow::New(); + window2->SetLineSmoothing(1); + window2->SetPointSmoothing(1); + window2->SetPolygonSmoothing(1); + mViewerA = ResliceImageViewer::New(); + mWidgetAxial->setRenderWindow(window2); + + auto window3 = vtkGenericOpenGLRenderWindow::New(); + window3->SetLineSmoothing(1); + window3->SetPointSmoothing(1); + window3->SetPolygonSmoothing(1); + mViewerS = ResliceImageViewer::New(); + mWidgetSagittal->setRenderWindow(window3); +} + +MPRResliceWindow::~MPRResliceWindow() { + +} + +void MPRResliceWindow::setRenderWindowLayout(LayoutType type) { + for (int i = 0 ; mMainSplitter && i < mMainSplitter->count(); ++i) { + mMainSplitter->replaceWidget(i,new QWidget); + } + for (int i = 0 ; mAppendSplitter && i < mAppendSplitter->count(); ++i) { + mAppendSplitter->replaceWidget(i,new QWidget); + } + + if (mMainSplitter)mMainSplitter->deleteLater(); + mMainSplitter = new QSplitter(this); + mMainSplitter->setHandleWidth(2); + layout()->addWidget(mMainSplitter); + mAppendSplitter = new QSplitter(this); + mAppendSplitter->setHandleWidth(2); + mMainSplitter->addWidget(mAppendSplitter); + switch (type) { + case Columns_1_1_1: + { + QWidget* widgets[3] = {mWidgetCoronal, mWidgetAxial, mWidgetSagittal}; + mAppendSplitter->setVisible(false); + mMainSplitter->setOrientation(Qt::Horizontal); + int width = this->width()/3; + + for (auto & widget : widgets) { + mMainSplitter->addWidget(widget); + } + QList sizes{0,width,width,width}; + mMainSplitter->setSizes(sizes); + mMainSplitter->setStretchFactor(0,0); + mMainSplitter->setStretchFactor(1,1); + mMainSplitter->setStretchFactor(2,1); + mMainSplitter->setStretchFactor(3,1); + break; + } + case Columns_1_2:{ + mMainSplitter->setOrientation(Qt::Horizontal); + mAppendSplitter->setVisible(true); + QWidget* widgets[2] = {mWidgetAxial,mWidgetSagittal }; + for (auto & widget : widgets) { + mAppendSplitter->addWidget(widget); + } + mMainSplitter->insertWidget(0,mWidgetCoronal); + mAppendSplitter->setOrientation(Qt::Vertical); + mAppendSplitter->setVisible(true); + QList sizes{width()/2, width()/2}; + mMainSplitter->setSizes(sizes); + mMainSplitter->setStretchFactor(0,1); + mMainSplitter->setStretchFactor(1,1); + QList sizes1{height()/2, height()/2}; + mAppendSplitter->setSizes(sizes1); + mAppendSplitter->setStretchFactor(0,1); + mAppendSplitter->setStretchFactor(1,1); + + break; + } + case Columns_2_1:{ + QWidget* widgets[2] = {mWidgetSagittal, mWidgetAxial}; + mMainSplitter->setOrientation(Qt::Horizontal); + mMainSplitter->addWidget(mWidgetCoronal); + mAppendSplitter->setOrientation(Qt::Vertical); + mAppendSplitter->setVisible(true); + for (auto & widget : widgets) { + mAppendSplitter->addWidget(widget); + } + + QList sizes{width()/2, width()/2}; + mMainSplitter->setSizes(sizes); + mMainSplitter->setStretchFactor(0,1); + mMainSplitter->setStretchFactor(1,1); + QList sizes1{height()/2, height()/2}; + mAppendSplitter->setSizes(sizes1); + mAppendSplitter->setStretchFactor(0,1); + mAppendSplitter->setStretchFactor(1,1); + + break; + } + case Rows_1_2:{ + QWidget* widgets[2] = {mWidgetAxial, mWidgetSagittal}; + mMainSplitter->setOrientation(Qt::Vertical); + mMainSplitter->insertWidget(0,mWidgetCoronal); + mAppendSplitter->setOrientation(Qt::Horizontal); + mAppendSplitter->setVisible(true); + for (auto & widget : widgets) { + mAppendSplitter->addWidget(widget); + } + QList sizes{height()/2, height()/2}; + mMainSplitter->setSizes(sizes); + mMainSplitter->setStretchFactor(0,1); + mMainSplitter->setStretchFactor(1,1); + QList sizes1{width()/2, width()/2}; + mAppendSplitter->setSizes(sizes1); + mAppendSplitter->setStretchFactor(0,1); + mAppendSplitter->setStretchFactor(1,1); + break; + } + case Rows_2_1:{ + QWidget* widgets[2] = {mWidgetAxial, mWidgetSagittal}; + mMainSplitter->setOrientation(Qt::Vertical); + mMainSplitter->addWidget(mWidgetCoronal); + mAppendSplitter->setOrientation(Qt::Horizontal); + mAppendSplitter->setVisible(true); + for (auto & widget : widgets) { + mAppendSplitter->addWidget(widget); + } + QList sizes{height()/2, height()/2}; + mMainSplitter->setSizes(sizes); + mMainSplitter->setStretchFactor(0,1); + mMainSplitter->setStretchFactor(1,1); + QList sizes1{width()/2, width()/2}; + mAppendSplitter->setSizes(sizes1); + mAppendSplitter->setStretchFactor(0,1); + mAppendSplitter->setStretchFactor(1,1); + break; + } + case Rows_1_1_1:{ + QWidget* widgets[3] = { mWidgetAxial, mWidgetSagittal,mWidgetCoronal}; + mAppendSplitter->setVisible(false); + mMainSplitter->setOrientation(Qt::Vertical); + for (auto & widget : widgets) { + mMainSplitter->addWidget(widget); + } + QList sizes{0,height()/3, height()/3, height()/3}; + mMainSplitter->setSizes(sizes); + mMainSplitter->setStretchFactor(0,0); + mMainSplitter->setStretchFactor(1,1); + mMainSplitter->setStretchFactor(2,1); + mMainSplitter->setStretchFactor(3,1); + break; + } + } +} + +void MPRResliceWindow::loadData(SeriesImageSet *series) { + mViewerC->SetRenderWindow(mWidgetCoronal->renderWindow()); + mViewerC->SetCoordsTransformMatrix(series->GetProperty()->GetOrientationMatrix()); + mViewerC->SetInputData(series->GetData()); + mViewerC->SetDefaultSliceOrientation(2); + mViewerC->Render(); + mViewerS->SetRenderWindow(mWidgetSagittal->renderWindow()); + mViewerS->SetCoordsTransformMatrix(series->GetProperty()->GetOrientationMatrix()); + mViewerS->SetInputData(series->GetData()); + mViewerS->SetDefaultSliceOrientation(1); + mViewerS->Render(); + mViewerA->SetRenderWindow(mWidgetAxial->renderWindow()); + mViewerA->SetCoordsTransformMatrix(series->GetProperty()->GetOrientationMatrix()); + mViewerA->SetInputData(series->GetData()); + mViewerA->SetDefaultSliceOrientation(0); + mViewerA->Render(); +} diff --git a/src/src/UI/Window/MPRResliceWindow.h b/src/src/UI/Window/MPRResliceWindow.h new file mode 100644 index 0000000..b85d75c --- /dev/null +++ b/src/src/UI/Window/MPRResliceWindow.h @@ -0,0 +1,49 @@ +// +// Created by Krad on 2022/12/8. +// + +#ifndef OMEGAV_MPRRESLICEWINDOW_H +#define OMEGAV_MPRRESLICEWINDOW_H + +#include +#include +#include + +class QVTKOpenGLNativeWidget; +class ResliceImageViewer; +class QSplitter; + + +class SeriesImageSet; + +class MPRResliceWindow :public QDialog { + Q_OBJECT + +public: + explicit MPRResliceWindow(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + ~MPRResliceWindow(); + enum LayoutType{ + Columns_1_2, + Columns_2_1, + Columns_1_1_1, + Rows_1_2, + Rows_2_1, + Rows_1_1_1, + }; + void loadData(SeriesImageSet* series); + void setRenderWindowLayout(LayoutType type); +private: + QVTKOpenGLNativeWidget *mWidgetAxial; + QVTKOpenGLNativeWidget *mWidgetSagittal; + QVTKOpenGLNativeWidget *mWidgetCoronal; + vtkSmartPointer mImageData = nullptr; + ResliceImageViewer *mViewerA = nullptr; + ResliceImageViewer *mViewerS = nullptr; + ResliceImageViewer *mViewerC = nullptr; + QSplitter* mMainSplitter= nullptr; + QSplitter* mAppendSplitter= nullptr; + int layoutType = 0; +}; + + +#endif //OMEGAV_MPRRESLICEWINDOW_H