一.楔子
这段时间一直有个问题没有解决就是在使用vtkAngleWidget的时候,想要在程序里面设置三个点的位置。
因为测量角度时候一直需要手动去选择三个点然后触发事件,但在三维空间中想要在二维屏幕上设置准确三个
点着实有点小蛋疼--!但是每次调用widget的setPosition时候发现会报错:null point 1 representation.
然后...
我果断忽略了,直接就百度、Google、bing各种但都没有找到合适的解答,在vtk user上找到有类似案例,
不过解决方案不明朗没有说清楚,否则如果照他们所说的我就都尝试过,且败了.
后来终于百度了这个报错,当然百度是百不到的,于是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的顶点更新角度值效果图