376 lines
12 KiB
C++
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);
|
|
}
|