vtk 自定义交互之 通过鼠标控制camera同时使用键盘控制actor

vtk 自定义交互之 通过鼠标控制camera同时使用键盘精确定量控制actor

场景需求:

vtk中可以使用 vtkInteractorStyleTrackballActor 交互器类来通过鼠标对单个actor的position进行调整,可以用于图像配准微调。另外,使用 vtkInteractorStyleTrackballCamera 类来通过鼠标对相机进行操作,调整观察整个vtk空间的角度。结合两者,可以实现调整观察角度和调整actor来进行某些操作,如动画,微调等。 但两者均通过鼠标来操作,不能同时操作。
虽然可以通过回调函数切换交互器来进行操作,但频繁操作不具有可行性,并且鼠标拖拽不能精确量化控制

本文实现通过键盘控制actor,同时 通过鼠标控制相机,实现类似于fps游戏的效果。

说明

特点:精确量化控制,同时操作相机和actor

注意事项:

  1. 键盘交互时会拾取焦点,确保焦点在想要移动的actor上。

  2. 当actor的pickable状态设置为false时,可屏蔽本操作。

  3. 如发现本代码有bug,烦请指出以改正。

实现效果

默认通过键盘 “w” “a” “s” “d” “e” “f” 控制actor旋转, 按 左ctrl 键在旋转和平移间切换,然后 “w” “a” “s” “d” “e” “f” 控制actor的平移。

展示见如下视频:

vtk类似fps游戏控制相机和actor演示

代码

本代码基于vtk8.2测试通过,

#include 

为导入vtk静态库的代码, 可通过别的方式加载。

ppzInteractorStyleTrackballActor.h


//ppzInteractorStyleTrackballActor.h

#ifndef PPZ_INTERACTORSTYLETRACKBALLACTOR_H
#define PPZ_INTERACTORSTYLETRACKBALLACTOR_H

#include 
#include 
#include 
#include 

#include 

class vtkCellPicker;

class InteractorStyleTrackballActor : public vtkInteractorStyleTrackballCamera
{
public:
    static InteractorStyleTrackballActor* New();
    vtkTypeMacro(InteractorStyleTrackballActor, vtkInteractorStyleTrackballCamera);
    void PrintSelf(ostream& os, vtkIndent indent) override;

    void OnLeftButtonDown() override;

    void Rotate(const double x,const double y, const double z) ;//旋转 沿屏幕

    void Pan(const double x, const double y, const double z);//平移 沿屏幕

    void OnChar() override;
    void OnKeyPress() override;


protected:
    InteractorStyleTrackballActor();
    ~InteractorStyleTrackballActor() override;

    void FindPickedActor(int x, int y);

    void Prop3DTransform(vtkProp3D* prop3D,
        double* boxCenter,
        int NumRotation,
        double** rotate,
        double* scale);

    double MotionFactor;

    vtkProp3D* InteractionProp;
    vtkCellPicker* InteractionPicker;

private:
    InteractorStyleTrackballActor(const InteractorStyleTrackballActor&) = delete;
    void operator=(const InteractorStyleTrackballActor&) = delete;

    bool m_isDoTranslation;

};

#endif

ppzInteractorStyleTrackballActor.cpp

//ppzInteractorStyleTrackballActor.cpp
#include "ppzInteractorStyleTrackballActor.h"

#include "vtkCamera.h"
#include "vtkCellPicker.h"
#include "vtkCallbackCommand.h"
#include "vtkMath.h"
#include "vtkMatrix4x4.h"
#include "vtkObjectFactory.h"
#include "vtkProp3D.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTransform.h"
vtkStandardNewMacro(InteractorStyleTrackballActor);

//----------------------------------------------------------------------------
InteractorStyleTrackballActor::InteractorStyleTrackballActor()
    : m_isDoTranslation(false)
{
    this->MotionFactor = 10.0;
    this->InteractionProp = nullptr;
    this->InteractionPicker = vtkCellPicker::New();
    this->InteractionPicker->SetTolerance(0.001);

}

//----------------------------------------------------------------------------
InteractorStyleTrackballActor::~InteractorStyleTrackballActor()
{
    this->InteractionPicker->Delete();
}

void InteractorStyleTrackballActor::Rotate(const double x, const double y,const double z)
{

    if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
    {
        return;
    }

    vtkRenderWindowInteractor* rwi = this->Interactor;
    vtkCamera* cam = this->CurrentRenderer->GetActiveCamera();

    // First get the origin of the assembly
    double* obj_center = this->InteractionProp->GetCenter();

    // GetLength gets the length of the diagonal of the bounding box
    double boundRadius = this->InteractionProp->GetLength() * 0.5;

    // Get the view up and view right vectors
    double view_up[3], view_look[3], view_right[3];

    cam->OrthogonalizeViewUp();
    cam->ComputeViewPlaneNormal();
    cam->GetViewUp(view_up);
    vtkMath::Normalize(view_up);
    cam->GetViewPlaneNormal(view_look);
    vtkMath::Cross(view_up, view_look, view_right);
    vtkMath::Normalize(view_right);

    // Get the furtherest point from object position+origin
    double outsidept[3];

    outsidept[0] = obj_center[0] + view_right[0] * boundRadius;
    outsidept[1] = obj_center[1] + view_right[1] * boundRadius;
    outsidept[2] = obj_center[2] + view_right[2] * boundRadius;

    // Convert them to display coord
    double disp_obj_center[3];

    this->ComputeWorldToDisplay(obj_center[0], obj_center[1], obj_center[2],
        disp_obj_center);

    this->ComputeWorldToDisplay(outsidept[0], outsidept[1], outsidept[2],
        outsidept);

    double radius = sqrt(vtkMath::Distance2BetweenPoints(disp_obj_center,
        outsidept));
    double nxf = (rwi->GetEventPosition()[0] - disp_obj_center[0]) / radius;

    double nyf = (rwi->GetEventPosition()[1] - disp_obj_center[1]) / radius;

    double oxf = (rwi->GetLastEventPosition()[0] - disp_obj_center[0]) / radius;

    double oyf = (rwi->GetLastEventPosition()[1] - disp_obj_center[1]) / radius;

    /* if (((nxf * nxf + nyf * nyf) <= 1.0) &&
         ((oxf * oxf + oyf * oyf) <= 1.0))*/
    {


        double scale[3];
        scale[0] = scale[1] = scale[2] = 1.0;

        double** rotate = new double* [3];

        rotate[0] = new double[4];
        rotate[1] = new double[4];
        rotate[2] = new double[4];

        rotate[0][0] = x;
        rotate[0][1] = view_up[0];
        rotate[0][2] = view_up[1];
        rotate[0][3] = view_up[2];


        rotate[1][0] = y;
        rotate[1][1] = view_right[0];
        rotate[1][2] = view_right[1];
        rotate[1][3] = view_right[2];

        rotate[2][0] = z;
        rotate[2][1] = view_look[0];
        rotate[2][2] = view_look[1];
        rotate[2][3] = view_look[2];

        this->Prop3DTransform(this->InteractionProp,
            obj_center,
            3,
            rotate,
            scale);

        delete[] rotate[0];
        delete[] rotate[1];
        delete[] rotate[2];
        delete[] rotate;

        if (this->AutoAdjustCameraClippingRange)
        {
            this->CurrentRenderer->ResetCameraClippingRange();
        }

        rwi->Render();
    }
}


void InteractorStyleTrackballActor::Pan(const double x, const double y, const double z)
{
    if (this->CurrentRenderer == nullptr || this->InteractionProp == nullptr)
    {
        return;
    }

    vtkRenderWindowInteractor* rwi = this->Interactor;

    // Use initial center as the origin from which to pan

    double* obj_center = this->InteractionProp->GetCenter();

    double disp_obj_center[3], new_pick_point[4];
    double old_pick_point[4], motion_vector[3];

    this->ComputeWorldToDisplay(obj_center[0], obj_center[1], obj_center[2],
        disp_obj_center);

    this->ComputeDisplayToWorld(
        disp_obj_center[0]+x,
        disp_obj_center[1]+y,
        disp_obj_center[2]+z,
        new_pick_point);

    this->ComputeDisplayToWorld(
        disp_obj_center[0],
        disp_obj_center[1],
        disp_obj_center[2],
        old_pick_point);


    //转为屏幕display物理坐标系的平移
    motion_vector[0] = new_pick_point[0] - old_pick_point[0];
    motion_vector[1] = new_pick_point[1] - old_pick_point[1];
    motion_vector[2] = new_pick_point[2] - old_pick_point[2];

    if (this->InteractionProp->GetUserMatrix() != nullptr)
    {
        vtkTransform* t = vtkTransform::New();
        t->PostMultiply();
        t->SetMatrix(this->InteractionProp->GetUserMatrix());
        t->Translate(motion_vector[0], motion_vector[1], motion_vector[2]);
        this->InteractionProp->GetUserMatrix()->DeepCopy(t->GetMatrix());
        t->Delete();
    }
    else
    {
        this->InteractionProp->AddPosition(motion_vector[0],
            motion_vector[1],
            motion_vector[2]);
    }

    if (this->AutoAdjustCameraClippingRange)
    {
        this->CurrentRenderer->ResetCameraClippingRange();
    }

    rwi->Render();
}


void InteractorStyleTrackballActor::OnChar()
{

    if (m_isDoTranslation)//平移
    {
        switch (this->Interactor->GetKeyCode())
        {
        case 'w'://上
            Pan(0, 1, 0);
            break;

        case 'a'://左
            Pan(-1, 0, 0);
            break;

        case 's'://下
            Pan(0, -1, 0);
            break;

        case 'd'://右
            Pan(1, 0, 0);
            break;

        case 'q'://前
            Pan(0, 0, 0.01);
            break;

        case 'e'://后
            Pan(0, 0, -0.01);
            break;
        }
    }

    else//旋转
    {
        switch (this->Interactor->GetKeyCode())
        {
        case 'w'://上
            Rotate(0, -3, 0);
            break;

        case 'a'://左
            Rotate(-3, 0, 0);
            break;

        case 's'://下
            Rotate(0, 3, 0);
            break;

        case 'd'://右
            Rotate(3, 0, 0);
            break;

        case 'q'://前
            Rotate(0, 0, 3);
            break;

        case 'e'://后
            Rotate(0, 0, -3);
            break;
        }
    }

}

void InteractorStyleTrackballActor::OnKeyPress()
{
    auto keyChar = this->Interactor->GetKeySym();
    const char* Control_L = "Control_L";
    auto isEqual = strcmp(keyChar, Control_L);
    if (isEqual == 0)
    {
        m_isDoTranslation = !m_isDoTranslation;
        if (m_isDoTranslation)
        {
            std::cout << "translation" << std::endl;
        }
        else
        {
            std::cout << "rotation" << std::endl;
        }
    }
}

//----------------------------------------------------------------------------
void InteractorStyleTrackballActor::PrintSelf(ostream& os, vtkIndent indent)
{
    this->Superclass::PrintSelf(os, indent);
}

void InteractorStyleTrackballActor::OnLeftButtonDown()
{
    int x = this->Interactor->GetEventPosition()[0];
    int y = this->Interactor->GetEventPosition()[1];

    this->FindPokedRenderer(x, y);
    this->FindPickedActor(x, y);

    vtkInteractorStyleTrackballCamera::OnLeftButtonDown();
}

//----------------------------------------------------------------------------
void InteractorStyleTrackballActor::FindPickedActor(int x, int y)
{
    this->InteractionPicker->Pick(x, y, 0.0, this->CurrentRenderer);
    vtkProp* prop = this->InteractionPicker->GetViewProp();
    if (prop != nullptr)
    {
        this->InteractionProp = vtkProp3D::SafeDownCast(prop);
    }
    else
    {
        this->InteractionProp = nullptr;
    }
}

//----------------------------------------------------------------------------
void InteractorStyleTrackballActor::Prop3DTransform(vtkProp3D* prop3D,
    double* boxCenter,
    int numRotation,
    double** rotate,
    double* scale)
{
    vtkMatrix4x4* oldMatrix = vtkMatrix4x4::New();
    prop3D->GetMatrix(oldMatrix);

    double orig[3];
    prop3D->GetOrigin(orig);

    vtkTransform* newTransform = vtkTransform::New();
    newTransform->PostMultiply();
    if (prop3D->GetUserMatrix() != nullptr)
    {
        newTransform->SetMatrix(prop3D->GetUserMatrix());
    }
    else
    {
        newTransform->SetMatrix(oldMatrix);
    }

    newTransform->Translate(-(boxCenter[0]), -(boxCenter[1]), -(boxCenter[2]));

    for (int i = 0; i < numRotation; i++)
    {
        newTransform->RotateWXYZ(rotate[i][0], rotate[i][1],
            rotate[i][2], rotate[i][3]);
    }

    if ((scale[0] * scale[1] * scale[2]) != 0.0)
    {
        newTransform->Scale(scale[0], scale[1], scale[2]);
    }

    newTransform->Translate(boxCenter[0], boxCenter[1], boxCenter[2]);

    // now try to get the composite of translate, rotate, and scale
    newTransform->Translate(-(orig[0]), -(orig[1]), -(orig[2]));
    newTransform->PreMultiply();
    newTransform->Translate(orig[0], orig[1], orig[2]);

    if (prop3D->GetUserMatrix() != nullptr)
    {
        newTransform->GetMatrix(prop3D->GetUserMatrix());
    }
    else
    {
        prop3D->SetPosition(newTransform->GetPosition());
        prop3D->SetScale(newTransform->GetScale());
        prop3D->SetOrientation(newTransform->GetOrientation());
    }
    oldMatrix->Delete();
    newTransform->Delete();
}

测试代码
testQuantitativeMovement.cpp

//testQuantitativeMovement.cpp
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
VTK_MODULE_INIT(vtkRenderingFreeType)
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);

//导入vtk库
#include 

#pragma comment(lib,"vtkRenderingOpenGL2-8.2.lib")

#include "ppzInteractorStyleTrackballActor.h"

int main()
{
    //圆柱
    vtkSmartPointer<vtkCylinderSource> cylinder =
        vtkSmartPointer<vtkCylinderSource>::New();
    cylinder->SetHeight(8.0);
    cylinder->SetRadius(2.0);
    cylinder->SetResolution(10);
    vtkSmartPointer<vtkPolyDataMapper> cylinderMapper =
        vtkSmartPointer<vtkPolyDataMapper>::New();
    cylinderMapper->SetInputConnection(cylinder->GetOutputPort());
    vtkSmartPointer<vtkActor> cylinderActor =
        vtkSmartPointer<vtkActor>::New();
    cylinderActor->SetPickable(false);
    cylinderActor->SetMapper(cylinderMapper);

    //圆柱2
    vtkSmartPointer<vtkCylinderSource> cylinder2 =
        vtkSmartPointer<vtkCylinderSource>::New();
    cylinder2->SetHeight(8.0);
    cylinder2->SetRadius(2.0);
    cylinder2->SetResolution(10);
    
    vtkSmartPointer<vtkPolyDataMapper> cylinderMapper2 =
        vtkSmartPointer<vtkPolyDataMapper>::New();
    cylinderMapper2->SetInputConnection(cylinder2->GetOutputPort());
    vtkSmartPointer<vtkActor> cylinderActor2 =
        vtkSmartPointer<vtkActor>::New();
    cylinderMapper2->ScalarVisibilityOff();
    cylinderActor2->SetMapper(cylinderMapper2);
    cylinderActor2->SetPosition(7,8,9);
    cylinderActor2->GetProperty()->SetColor(1.0000, 0.3882, 0.2784);

    //坐标轴
    vtkNew<vtkAxesActor> axesActor;
    axesActor->SetTotalLength(10, 10, 10);

    vtkSmartPointer<vtkRenderer> renderer =
        vtkSmartPointer<vtkRenderer>::New();
    renderer->AddActor(cylinderActor);
    renderer->AddActor(cylinderActor2);
    renderer->AddActor(axesActor);
    renderer->SetBackground(0.1, 0.2, 0.4);

    vtkSmartPointer<vtkRenderWindow> renWin =
        vtkSmartPointer<vtkRenderWindow>::New();
    renWin->AddRenderer(renderer);
    renWin->SetSize(800, 800);

    vtkSmartPointer<vtkRenderWindowInteractor> iren =
        vtkSmartPointer<vtkRenderWindowInteractor>::New();

    InteractorStyleTrackballActor* myStyleActor = //自定义交互  
        InteractorStyleTrackballActor::New();

    iren->SetRenderWindow(renWin);
    iren->SetInteractorStyle(myStyleActor);

    iren->Initialize();

    iren->Start();

    return EXIT_SUCCESS;
}


你可能感兴趣的:(医学图像处理,VTK,交互,vtk,c++)