[VTK]vtkAngleWidget角度测量设置point位置

一.楔子

这段时间一直有个问题没有解决就是在使用vtkAngleWidget的时候,想要在程序里面设置三个点的位置。

因为测量角度时候一直需要手动去选择三个点然后触发事件,但在三维空间中想要在二维屏幕上设置准确三个

点着实有点小蛋疼--!但是每次调用widget的setPosition时候发现会报错:null point 1 representation.

然后...

我果断忽略了,直接就百度、Google、bing各种但都没有找到合适的解答,在vtk user上找到有类似案例,

http://vtk.1045678.n5.nabble.com/Re-How-to-initialize-vtkAngleWidget-with-three-points-specified-td3281326.html

不过解决方案不明朗没有说清楚,否则如果照他们所说的我就都尝试过,且败了.

后来终于百度了这个报错,当然百度是百不到的,于是google之,go到了vtk的源代码。

http://stuff.mit.edu/afs/athena/course/16/16.225/dv/VTK/Widgets/vtkAngleRepresentation3D.cxx

里面提到:

void vtkAngleRepresentation3D::SetCenterWorldPosition(double x[3])
{
   if (!this->CenterRepresentation)
    {
    vtkErrorMacro("SetCenterWorldPosition: null center representation");
    return;
    }
  this->CenterRepresentation->SetWorldPosition(x);
}

从代码里面可以看出在设置时候会先判断本身的成员CenterRepresentation是否为空,那么CenterRepresentation又是何方高人呢?

继续Follow之,发现在vtkAngleRepresentation(抽象类)里面定义了四个成员变量:

(注:源代码可以通过修改后缀在网上查看:如这里与上面的链接就只有后面的文件名不同的

http://stuff.mit.edu/afs/athena/course/16/16.225/dv/VTK/Widgets/vtkAngleRepresentation3D.h

// The handle and the rep used to close the handles
vtkHandleRepresentation *HandleRepresentation;
vtkHandleRepresentation *Point1Representation;
vtkHandleRepresentation *CenterRepresentation;
vtkHandleRepresentation *Point2Representation;

但是在vtkAngleRepresentation3D.的构造函数里面只有:

  // By default, use one of these handles
  this->HandleRepresentation  = vtkPointHandleRepresentation3D::New();

难怪会报那样的错呢--!

(这里就不对以前自己完全忽略错误提示的行为...)

2.解决方案

我的粗劣的解决方案是自己继承一个vtkAngleRepresentation3D类,然后对其余的三个representation进行初始化,再在后面对他们进行修改即可。

// GitPro.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <vtkCommand.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkSphereSource.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkClipPolyData.h>
#include <vtkActor.h>
#include <vtkConeSource.h>
#include <vtkPlane.h>
#include <vtkImageActor.h>
#include <vtkImplicitPlaneWidget2.h>
#include <vtkAngleWidget.h>
#include <vtkPlaneWidget.h>
#include <vtkSmartPointer.h>
#include <vtkImplicitPlaneRepresentation.h>
#include <vtkAngleRepresentation3D.h>
#include <vtkPointHandleRepresentation3D.h>
#include <vtkInteractorStyleTrackballCamera.h>

#define PLANE_LEFT

class vtkAngleRepresentation3DWithPointRepresentation: public vtkAngleRepresentation3D
{
    public:
     static vtkAngleRepresentation3DWithPointRepresentation *New()
     {
          return new vtkAngleRepresentation3DWithPointRepresentation;
     }
     void initialPointRepresentation()
     {
         this->Point1Representation = vtkPointHandleRepresentation3D::New();
         this->Point2Representation = vtkPointHandleRepresentation3D::New();
         this->CenterRepresentation = vtkPointHandleRepresentation3D::New();
     }
};


class VTKWidgetCall : public vtkCommand
{
public:

    static VTKWidgetCall *New()
    {
    return new VTKWidgetCall;
    }
public:
    virtual void Execute(vtkObject *caller, unsigned long eventId, void *callData)
    {
        vtkImplicitPlaneWidget2 *pWidget = reinterpret_cast<vtkImplicitPlaneWidget2*>(caller);
        if (pWidget)
        {
            // update the clip plane and the second renderer
             vtkImplicitPlaneRepresentation *rep = 
             reinterpret_cast<vtkImplicitPlaneRepresentation*>(pWidget->GetRepresentation());

            vtkSmartPointer<vtkPlane> planeNew = vtkPlane::New();
            rep->GetPlane(planeNew);
        
            cliper->SetClipFunction(planeNew);
            cliper->Update();

            vtkSmartPointer<vtkPolyData> clipedData = vtkPolyData::New();
            clipedData->DeepCopy(cliper->GetOutput());

            vtkSmartPointer<vtkPolyDataMapper> coneMapper = vtkPolyDataMapper::New();
            coneMapper->SetInput(clipedData);
            actor->SetMapper(coneMapper);

            // calculate three point to place the angleWidget
            double* position1 = planeNew->GetOrigin();
            double  position2[3] = {position1[0], position1[1], position1[2] + 1};
            double  position3[3] = {position1[0], position1[1] + 1, position1[2]};
            // you can modify the 3 points like this(Notice:you need to use SetWorldPosition not SetDisplayPosition you could try and look)
            angleWidget->GetAngleRepresentation()->GetPoint1Representation()->SetWorldPosition(position1);
            angleWidget->GetAngleRepresentation()->GetPoint2Representation()->SetWorldPosition(position2);
            angleWidget->GetAngleRepresentation()->GetCenterRepresentation()->SetWorldPosition(position3);
            // There is a bug here, that the angle did not calculated or updated. But in my other 
            // test it act well. I don't know why...
        }
    }
    void setCliper(vtkSmartPointer<vtkClipPolyData> other){cliper = other;}
    void setPlane(vtkSmartPointer<vtkPlane> other){pPlane = other;}
    void setActor(vtkSmartPointer<vtkActor> other){actor = other;}
    void setAngleWidget(vtkSmartPointer<vtkAngleWidget> other){angleWidget = other;}
private:
    vtkSmartPointer<vtkPlane> pPlane;
    vtkSmartPointer<vtkActor> actor;
    vtkSmartPointer<vtkClipPolyData> cliper;
    vtkSmartPointer<vtkAngleWidget> angleWidget;
};


int _tmain(int argc, _TCHAR* argv[])
{
    vtkSmartPointer<vtkSphereSource> sphereSource = 
    vtkSmartPointer<vtkSphereSource>::New();
    sphereSource->Update();
 
    // Create a mapper and actor
    vtkSmartPointer<vtkPolyDataMapper> mapper = 
    vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputConnection(sphereSource->GetOutputPort());
    vtkSmartPointer<vtkActor> actor = 
    vtkSmartPointer<vtkActor>::New();
 
    // A renderer and render window
    vtkSmartPointer<vtkRenderer> renderer = 
    vtkSmartPointer<vtkRenderer>::New();
    vtkSmartPointer<vtkRenderWindow> renderWindow = 
    vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->AddRenderer(renderer);
    renderer->SetBackground( 0.1, 0.2, 0.4 );
    renderer->SetViewport(0.0, 0.0, 0.5, 1.0);
    actor->SetMapper(mapper);
    renderer->AddActor(actor);
 
    // An interactor
    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = 
    vtkSmartPointer<vtkRenderWindowInteractor>::New();
    renderWindowInteractor->SetRenderWindow(renderWindow);
 
    vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = 
    vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
 
    renderWindowInteractor->SetInteractorStyle( style );
    double origin[3] = {0, 100, 0};

      vtkSmartPointer<vtkImplicitPlaneRepresentation> rep = 
    vtkSmartPointer<vtkImplicitPlaneRepresentation>::New();
    rep->SetPlaceFactor(1.25); // This must be set prior to placing the widget
    rep->PlaceWidget(actor->GetBounds());
    
    vtkSmartPointer<vtkImplicitPlaneWidget2> implicitPlaneWidget = vtkImplicitPlaneWidget2::New();
    implicitPlaneWidget->SetInteractor(renderWindowInteractor);
    implicitPlaneWidget->SetRepresentation(rep);
    /*this code set the default render of the planeWidget*/
    //implicitPlaneWidget->SetCurrentRenderer(renderer);
    vtkSmartPointer<vtkPlane> planeNew = vtkPlane::New();
    vtkSmartPointer<vtkClipPolyData> cliper = vtkClipPolyData::New();
    cliper->SetInput(sphereSource->GetOutput());
    cliper->SetClipFunction(planeNew);
    cliper->Update();

    vtkSmartPointer<vtkPolyData> clipedData = vtkPolyData::New();
    clipedData->DeepCopy(cliper->GetOutput());

    /*Codes below set the second renderer*/
    vtkSmartPointer<vtkConeSource> cone = vtkConeSource::New();
    cone->SetHeight( 3.0 );
    cone->SetRadius( 1.0 );
    cone->SetResolution( 10 );
    vtkSmartPointer<vtkPolyDataMapper> coneMapper = vtkPolyDataMapper::New();
    coneMapper->SetInput(clipedData);
    vtkSmartPointer<vtkActor> coneActor = vtkActor::New();
    coneActor->SetMapper( mapper );

    vtkSmartPointer<vtkRenderer> rRenderer = 
    vtkSmartPointer<vtkRenderer>::New();
    rRenderer->SetBackground( 0.2, 0.3, 0.5 );
    rRenderer->SetViewport(0.5, 0.0, 1.0, 1.0);
    rRenderer->AddActor(coneActor);

    renderWindow->AddRenderer(rRenderer);
    
#ifdef PLANE_LEFT
    /*if you did not use implicitPlaneWidget->SetCurrentRenderer(renderer)
      then the default render of the plane is different with the loaction of this sentence。
      You could try and see. 
    */
    renderWindow->Render();
#endif
    vtkSmartPointer<VTKWidgetCall> pCall = VTKWidgetCall::New();
    pCall->setPlane(planeNew);
    pCall->setActor(coneActor);
    pCall->setCliper(cliper);
    implicitPlaneWidget->AddObserver(vtkCommand::EndInteractionEvent, pCall);
    implicitPlaneWidget->On();

      vtkSmartPointer<vtkAngleRepresentation3DWithPointRepresentation> rep3D = vtkSmartPointer<vtkAngleRepresentation3DWithPointRepresentation>::New();
    vtkSmartPointer<vtkAngleWidget> angleWidget = vtkSmartPointer<vtkAngleWidget>::New();
    rep3D->initialPointRepresentation();
    angleWidget->SetRepresentation(rep3D);
    angleWidget->SetInteractor(renderWindowInteractor);
    //angleWidget->SetCurrentRenderer(rRenderer);
    angleWidget->On();
    pCall->setAngleWidget(angleWidget);

#ifndef PLANE_LEFT
    renderWindow->Render();
#endif
    renderWindowInteractor->Initialize();
    // Begin mouse interaction
    renderWindowInteractor->Start();
    return EXIT_SUCCESS;
}

上面代码做的有这样几件事:

一:使用默认的model进行了显示

二:设置了切割平面进行切割

三:切割后结果显示在右边的第二个renderer里面

四:添加了角度测量的widget进行测量

五:当移动切割平面时候widget每次都会自动设置到与平面相关的一个位置(这个就是我这里想说明的东东)

六:有个bug,就是在这个例子里面每次设置了位置后角度值并没有更新,而我另外一个例子(太乱的一个原始例子里面)其值是得到了更新的

      而在这里需要再对几个点移动一点点才会更新

三:The end

下面是结果图,第一组是初始化的手动设置的三个点;第二组是移动了切割平面后;第三组是手动再挪动了一下angleWidget的顶点更新角度值效果图



原文地址:https://www.cnblogs.com/dawnWind/p/3D_10.html