Add ResliceImageViewer, ResliceImageInteractorStyle, ResliceCursorLegendActor, MPRResliceWindow.
This commit is contained in:
45
src/src/Interaction/ResliceImageInteractorStyle.cpp
Normal file
45
src/src/Interaction/ResliceImageInteractorStyle.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Created by Krad on 2022/12/6.
|
||||
//
|
||||
|
||||
#include "ResliceImageInteractorStyle.h"
|
||||
|
||||
#include <vtkObjectFactory.h>
|
||||
#include <vtkCommand.h>
|
||||
#include <vtkCallbackCommand.h>
|
||||
#include <vtkRenderWindow.h>
|
||||
#include <vtkRenderWindowInteractor.h>
|
||||
#include <vtkCamera.h>
|
||||
#include <vtkPropCollection.h>
|
||||
|
||||
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;
|
||||
}
|
||||
39
src/src/Interaction/ResliceImageInteractorStyle.h
Normal file
39
src/src/Interaction/ResliceImageInteractorStyle.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Created by Krad on 2022/12/6.
|
||||
//
|
||||
|
||||
#ifndef RENDERLAB_RESLICEIMAGEINTERACTORSTYLE_H
|
||||
#define RENDERLAB_RESLICEIMAGEINTERACTORSTYLE_H
|
||||
|
||||
#include <vtkInteractorStyleImage.h>
|
||||
|
||||
#include <vector>
|
||||
#include <vtkRenderer.h>
|
||||
|
||||
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
|
||||
233
src/src/Rendering/Legend/ResliceCursorLegendActor.cpp
Normal file
233
src/src/Rendering/Legend/ResliceCursorLegendActor.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
//
|
||||
// Created by Krad on 2022/12/1.
|
||||
//
|
||||
|
||||
#include "ResliceCursorLegendActor.h"
|
||||
|
||||
#include <vtkObjectFactory.h>
|
||||
#include <vtkPolyDataMapper2D.h>
|
||||
#include <vtkRenderer.h>
|
||||
#include <vtkRenderWindow.h>
|
||||
#include <vtkRenderWindowInteractor.h>
|
||||
#include <vtkInteractorStyle.h>
|
||||
#include <vtkCamera.h>
|
||||
#include <vtkPoints.h>
|
||||
#include <vtkProperty2D.h>
|
||||
#include <vtkActor2D.h>
|
||||
#include <vtkPolyLineSource.h>
|
||||
#include <vtkProperty2D.h>
|
||||
#include <vtkDiskSource.h>
|
||||
|
||||
#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<vtkPoints> 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<vtkPolyDataMapper2D> mapper ;
|
||||
vtkNew<vtkPolyDataMapper2D> mapper1 ;
|
||||
vtkNew<vtkPolyDataMapper2D> 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();
|
||||
}
|
||||
98
src/src/Rendering/Legend/ResliceCursorLegendActor.h
Normal file
98
src/src/Rendering/Legend/ResliceCursorLegendActor.h
Normal file
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// Created by Krad on 2022/12/1.
|
||||
//
|
||||
|
||||
#ifndef RENDERLAB_RESLICECURSORLEGENDACTOR_H
|
||||
#define RENDERLAB_RESLICECURSORLEGENDACTOR_H
|
||||
|
||||
#include <vtkProp.h>
|
||||
|
||||
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
|
||||
233
src/src/Rendering/Viewer/ResliceImageViewer.cpp
Normal file
233
src/src/Rendering/Viewer/ResliceImageViewer.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
//
|
||||
// Created by Krad on 2022/12/8.
|
||||
//
|
||||
|
||||
#include "ResliceImageViewer.h"
|
||||
|
||||
#include <vtkObjectFactory.h>
|
||||
#include <vtkRenderer.h>
|
||||
#include <vtkRenderWindow.h>
|
||||
#include <vtkRenderWindowInteractor.h>
|
||||
#include <vtkCamera.h>
|
||||
#include <vtkImageData.h>
|
||||
#include <vtkInteractorStyleImage.h>
|
||||
#include <vtkImageResliceMapper.h>
|
||||
#include <vtkImageSlice.h>
|
||||
#include <vtkMatrix4x4.h>
|
||||
#include <vtkProperty2D.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
73
src/src/Rendering/Viewer/ResliceImageViewer.h
Normal file
73
src/src/Rendering/Viewer/ResliceImageViewer.h
Normal file
@@ -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
|
||||
219
src/src/UI/Window/MPRResliceWindow.cpp
Normal file
219
src/src/UI/Window/MPRResliceWindow.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
//
|
||||
// Created by Krad on 2022/12/8.
|
||||
//
|
||||
|
||||
#include "MPRResliceWindow.h"
|
||||
|
||||
#include <vtkGenericOpenGLRenderWindow.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <QGuiApplication.h>
|
||||
#include <QVTKOpenGLNativeWidget.h>
|
||||
#include <QGridLayout>
|
||||
#include <QMenuBar>
|
||||
#include <QSplitter>
|
||||
|
||||
#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<int> 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<int> sizes{width()/2, width()/2};
|
||||
mMainSplitter->setSizes(sizes);
|
||||
mMainSplitter->setStretchFactor(0,1);
|
||||
mMainSplitter->setStretchFactor(1,1);
|
||||
QList<int> 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<int> sizes{width()/2, width()/2};
|
||||
mMainSplitter->setSizes(sizes);
|
||||
mMainSplitter->setStretchFactor(0,1);
|
||||
mMainSplitter->setStretchFactor(1,1);
|
||||
QList<int> 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<int> sizes{height()/2, height()/2};
|
||||
mMainSplitter->setSizes(sizes);
|
||||
mMainSplitter->setStretchFactor(0,1);
|
||||
mMainSplitter->setStretchFactor(1,1);
|
||||
QList<int> 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<int> sizes{height()/2, height()/2};
|
||||
mMainSplitter->setSizes(sizes);
|
||||
mMainSplitter->setStretchFactor(0,1);
|
||||
mMainSplitter->setStretchFactor(1,1);
|
||||
QList<int> 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<int> 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();
|
||||
}
|
||||
49
src/src/UI/Window/MPRResliceWindow.h
Normal file
49
src/src/UI/Window/MPRResliceWindow.h
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Created by Krad on 2022/12/8.
|
||||
//
|
||||
|
||||
#ifndef OMEGAV_MPRRESLICEWINDOW_H
|
||||
#define OMEGAV_MPRRESLICEWINDOW_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkImageData.h>
|
||||
|
||||
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<vtkImageData> mImageData = nullptr;
|
||||
ResliceImageViewer *mViewerA = nullptr;
|
||||
ResliceImageViewer *mViewerS = nullptr;
|
||||
ResliceImageViewer *mViewerC = nullptr;
|
||||
QSplitter* mMainSplitter= nullptr;
|
||||
QSplitter* mAppendSplitter= nullptr;
|
||||
int layoutType = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif //OMEGAV_MPRRESLICEWINDOW_H
|
||||
Reference in New Issue
Block a user