Skip to content

PointHandleRepresentation3D无法移动手柄

之前用VTK8实现了一些自己写的交互,最近升级到VTK9,发现有些不能使用了。

1 原来的功能

屏幕中放置一个手柄,设置其尺寸为0(或者隐藏)。当鼠标移入这个隐藏的手柄时候会改变形状,选中拖动后会触发别的事情实时改变。

2 升级9后遇到的问题

隐藏的手柄无移入效果和无法被拖动。

3 分析问题

定位:VTK9里 vtkPointHandleRepresentation3D 这个类,如果设置 隐藏/尺寸为0 后。他的vtkPointHandleRepresentation3D::ComputeInteractionState 永远返回vtkHandleRepresentation::Outside。对比了下VTK8和VTK9的源码,发现新版本 vtkPointHandleRepresentation3D做了一个改动:无论什么情况,首先确保光标位于显示空间中表示的边界球体内。这个MR是为了修复一个视角相关的BUG。

相关MR https://gitlab.kitware.com/vtk/vtk/-/commit/1dca39384ab4940798471ec1da0a3877df9ff726

4 解决办法

原来的代码

CBvtkResliceWidgetRepresentation::CBvtkResliceWidgetRepresentation() {
    m_centerMovementPointRepresentation =
        vtkSmartPointer<vtkPointHandleRepresentation3D>::New();
    m_centerMovementPointRepresentation->SetSmoothMotion(1);
    m_centerMovementPointRepresentation->SetHandleSize(0);
    m_centerMovementPointRepresentation->SetTolerance(15);
    m_centerMovementPointRepresentation->AllOff();
    m_centerMovementPointRepresentation->TranslationModeOn();
    m_cursorActor = vtkSmartPointer<CBvtkResliceActor>::New();
    m_cursorVisibility = true;
}

int CBvtkResliceWidgetRepresentation::ComputeInteractionState(
    const int X, const int Y, int ) {
    if (m_centerMovementPointRepresentation->ComputeInteractionState(X, Y)
            != vtkHandleRepresentation::Outside) {
        return handleCursor;
    };
    vtkNew<vtkCellPicker> picker;
    picker->SetTolerance(0.01);
    picker->InitializePickList();
    picker->PickFromListOn();
    picker->AddPickList(m_cursorActor->getActor());
    if (picker->Pick(X, Y, 0, Renderer)) {
        return mprCursor;
    }
    return outside;
}

void CBvtkResliceWidgetRepresentation::BuildRepresentation() {
    if (GetMTime() > BuildTime ||
            m_centerMovementPointRepresentation->GetMTime() > BuildTime ||
            (Renderer &&
             Renderer->GetVTKWindow() &&
             Renderer->GetVTKWindow()->GetMTime() > BuildTime)
       ) {
        double centerPos[3];
        m_centerMovementPointRepresentation->GetWorldPosition(centerPos);
        Renderer->GetVTKWindow()->GetSize();
        m_cursorActor->setCameraDistance(
            Renderer->GetActiveCamera()->GetDistance());
        vtkNew<vtkCoordinate> coord;
        coord->SetCoordinateSystemToDisplay();
        coord->SetValue(
            Renderer->GetVTKWindow()->GetSize()[0],
            Renderer->GetVTKWindow()->GetSize()[1], 0);
        double *size = coord->GetComputedWorldValue(Renderer);
        m_cursorActor->setCenterPosition(centerPos);
        m_cursorActor->setDisplaySize(size);
        coord->SetValue(0, 0);
        double *origin = coord->GetComputedWorldValue(Renderer);
        m_cursorActor->setDisplayOriginPoint(origin);
        m_cursorActor->update();
        BuildTime.Modified();
    }
}

我目前先用了个比较蠢的办法:直接给他还原回去吧。

class CBvtkPointHandleRepresentation3D : public vtkPointHandleRepresentation3D {
  public:
    static CBvtkPointHandleRepresentation3D *New();
    vtkTypeMacro(CBvtkPointHandleRepresentation3D, vtkPointHandleRepresentation3D);
    CBvtkPointHandleRepresentation3D() = default;
    ~CBvtkPointHandleRepresentation3D() = default;
    int ComputeInteractionState(int X, int Y, int modify) override;
};

int CBvtkPointHandleRepresentation3D::ComputeInteractionState(int X, int Y, int ) {
    this->VisibilityOn();
    vtkAssemblyPath *path = this->GetAssemblyPath(X, Y, 0., this->CursorPicker);
    double focus[3];
    this->Cursor3D->GetFocalPoint(focus);
    double d[3];
    this->GetDisplayPosition(d);
    if ( path != nullptr ) {
        this->InteractionState = vtkHandleRepresentation::Nearby;
    } else {
        this->InteractionState = vtkHandleRepresentation::Outside;
        if ( this->ActiveRepresentation ) {
            this->VisibilityOff();
        }
    }
    return this->InteractionState;
}