Files
DCMV/src/src/Rendering/Legend/ResliceCursorLegendActor.cpp
2022-12-29 09:55:22 +08:00

376 lines
12 KiB
C++

//
// 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 <vtkPlane.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(vtkActor2D::New())
, ControlPointSenseArea(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);
vtkNew<vtkDiskSource> disk;
disk->SetInnerRadius(0);
disk->SetOuterRadius(5);
disk->SetCircumferentialResolution(36);
disk->SetRadialResolution(36);
vtkNew<vtkDiskSource> diskSense;
diskSense->SetInnerRadius(0);
diskSense->SetOuterRadius(10);
diskSense->SetCircumferentialResolution(36);
diskSense->SetRadialResolution(36);
mapper1->SetInputConnection(disk->GetOutputPort());
mapper2->SetInputConnection(diskSense->GetOutputPort());
LineActor->SetMapper(mapper);
LineActor->GetProperty()->SetLineWidth(2);
ControlPoint->SetMapper(mapper1);
ControlPoint->SetProperty(LineActor->GetProperty());
ControlPointSenseArea->SetMapper(mapper2);
ControlPointSenseArea->GetProperty()->SetOpacity(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) {
if(dragging){
Drag(renderer);
}
if (LoadTime.GetMTime()>MTime.GetMTime()) return;
//handle disk
if (HandleUpdated) {
renderer->SetDisplayPoint(Handle2DPoint[0], Handle2DPoint[1], 0);
renderer->DisplayToWorld();
renderer->GetWorldPoint(HandlePoint);
HandleUpdated = false;
ControlPoint->SetPosition(Handle2DPoint[0] - 0.5, Handle2DPoint[1] - .5);
ControlPointSenseArea->SetPosition(Handle2DPoint[0], Handle2DPoint[1] - .5);
return;
}
//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 out2[4] = {.0, .0, .0, .0};
linePolyData->GetPoints()->SetNumberOfPoints(2);
renderer->SetWorldPoint(point1);
renderer->WorldToDisplay();
renderer->GetDisplayPoint(SliceDisplayPoint);
renderer->SetWorldPoint(point2);
renderer->WorldToDisplay();
renderer->GetDisplayPoint(out2);
double vector2D[2] = {out2[0] - SliceDisplayPoint[0], out2[1] - SliceDisplayPoint[1]};
vtkMath::Normalize2D(vector2D);
Vector2 v1 = {vector2D[0], vector2D[1]};
Vector2 screenVectors[2] = {{0, 1},
{1, 0}};
Vector2 p0 = {SliceDisplayPoint[0], SliceDisplayPoint[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){
LoadTime.Modified();
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.05,
(int) intersectPoint.Y + 0.05, 0);
}
}
}
}
LoadTime.Modified();
}
void ResliceCursorLegendActor::ReleaseGraphicsResources(vtkWindow * window) {
ControlPointSenseArea->ReleaseGraphicsResources(window);
LineActor->ReleaseGraphicsResources(window);
ControlPoint->ReleaseGraphicsResources(window);
vtkProp::ReleaseGraphicsResources(window);
}
int ResliceCursorLegendActor::RenderOverlay(vtkViewport *viewport) {
auto renderer = vtkRenderer::SafeDownCast(viewport);
if (!renderer) return 0;
BuildShape(renderer);
if (LineActor->GetVisibility())LineActor->RenderOverlay(viewport);
if (ControlPointActive){
if (ControlPointSenseArea->GetVisibility())ControlPointSenseArea->RenderOverlay(viewport);
if (ControlPoint->GetVisibility())ControlPoint->RenderOverlay(viewport);
}
return 1;
}
vtkProperty2D *ResliceCursorLegendActor::GetProperty() {
return LineActor->GetProperty();
}
void ResliceCursorLegendActor::UpdateMousePosition(int* pos) {
ControlPointActive = true;
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();
}
void ResliceCursorLegendActor::GetActors(vtkPropCollection * collection) {
collection->AddItem(ControlPointSenseArea);
}
void ResliceCursorLegendActor::Drag(vtkRenderer* renderer) {
int *pos = renderer->GetRenderWindow()->GetInteractor()->GetEventPosition();
int distance2 =pow((DragStartPosition[0] - (int)(SliceDisplayPoint[0])),2) + pow((DragStartPosition[1] - (int)(SliceDisplayPoint[1])),2);
if (distance2<=225 || Moving){
ApplyMove(renderer);
Moving = true;
}
else{
ApplyRoll(renderer);
Rolling = true;
}
}
void ResliceCursorLegendActor::ApplyRoll(vtkRenderer * renderer) {
int *pos = renderer->GetRenderWindow()->GetInteractor()->GetEventPosition();
double slicePt[4] = {SlicePoint[0] ,
SlicePoint[1] ,
SlicePoint[2],
1.0};;
SliceDisplayPoint[2] = 0.0;
SliceDisplayPoint[3] = 1.0;
renderer->SetDisplayPoint(SliceDisplayPoint);
renderer->DisplayToWorld();
renderer->GetWorldPoint(slicePt);
double position [4] ={ pos[0]+0.05, pos[1]+0.05,0,1};
double vector2D[2] = {position[0] - SliceDisplayPoint[0], position[1] - SliceDisplayPoint[1]};
vtkMath::Normalize2D(vector2D);
renderer->SetDisplayPoint(position);
renderer->DisplayToWorld();
renderer->GetWorldPoint(position);
double newVector[3]={position[0] - slicePt[0], position[1] - slicePt[1], position[2] - slicePt[2]};
vtkMath::Normalize(newVector);
if (ReferenceCursor){
ReferenceCursor->SetSliceDirectionVector(newVector);
}
vtkMath::Cross(newVector, GetProjectDirectionVector(), newVector);
this->SetSliceDirectionVector(newVector);
}
void ResliceCursorLegendActor::ApplyMove(vtkRenderer * renderer) {
int *pos = renderer->GetRenderWindow()->GetInteractor()->GetEventPosition();
if (!Moving){
LastSliceDisplayPoint[0]=SliceDisplayPoint[0];
LastSliceDisplayPoint[1]=SliceDisplayPoint[1];
}
double pt1[4] = {LastSliceDisplayPoint[0]+(pos[0]-DragStartPosition[0]) + 0.5 ,
LastSliceDisplayPoint[1]+(pos[1]-DragStartPosition[1]) + 0.5 ,
0,
1.0};
renderer->SetDisplayPoint(pt1);
renderer->DisplayToWorld();
renderer->GetWorldPoint(pt1);
double pt2[4] = {pt1[0] + 10000.0 * ProjectDirectionVector[0],
pt1[1] + 10000.0 * ProjectDirectionVector[1],
pt1[2] + 10000.0 * ProjectDirectionVector[2],
1.0};
double x ;
double intersectPoint[4]={.0,.0,.0,1.};
if (vtkPlane::IntersectWithLine(pt1, pt2, ProjectDirectionVector, SlicePoint,x,intersectPoint))
{
this->SetSlicePoint(intersectPoint);
if (ReferenceCursor){
ReferenceCursor->SetSlicePoint(intersectPoint);
}
this->InvokeEvent(MOVE);
}
}
void ResliceCursorLegendActor::InvokeDragEvent() {
if (Rolling){
this->InvokeEvent(ROLL);
return;
}
if (Moving){
this->InvokeEvent(MOVE);
return;
}
}
void ResliceCursorLegendActor::UpdateCursor3DPoint(vtkRenderer * renderer) {
double pt1[4] = {.0, .0, .0,.0};
linePolyData->GetPoints()->GetPoint(0, pt1);
double pt2[4] = {.0, .0, .0, .0};
linePolyData->GetPoints()->GetPoint(1, pt2);
double worldPt1[4] = {.0,.0,.0,1.};
double worldPt2[4] = {.0,.0,.0,1.};
renderer->SetDisplayPoint(pt1);
renderer->DisplayToWorld();
renderer->GetWorldPoint(worldPt1);
renderer->SetDisplayPoint(pt2);
renderer->DisplayToWorld();
renderer->GetWorldPoint(worldPt2);
vtkNew<vtkPlane> plane;
plane->SetOrigin(GetSlicePoint());
plane->SetNormal(GetProjectDirectionVector());
double t;
double wpt_end[4] = {worldPt1[0] + 10000.0 * ProjectDirectionVector[0],
worldPt1[1] + 10000.0 * ProjectDirectionVector[1],
worldPt1[2] + 10000.0 * ProjectDirectionVector[2],
1.0};
double newPt1[4] = {.0,.0,.0,1.};
int ret = plane->IntersectWithLine(worldPt1,wpt_end,t,newPt1);
double wpt_end2[4] = {worldPt2[0] + 10000.0 * ProjectDirectionVector[0],
worldPt2[1] + 10000.0 * ProjectDirectionVector[1],
worldPt2[2] + 10000.0 * ProjectDirectionVector[2],
1.0};
double newPt2[4] = {.0,.0,.0,1.};
ret = plane->IntersectWithLine(worldPt2,wpt_end2,t,newPt2);
double vector[3] = {worldPt2[0]-worldPt1[0],worldPt2[1]-worldPt1[1],worldPt2[2]-worldPt1[2]};
vtkMath::Cross(vector, GetProjectDirectionVector(), vector);
this->SetSliceDirectionVector(vector);
}