【VTK】vtk actor空間變換後的點對映與向量對映
在三維空間中,人們通常需要對平面等形體進行旋轉,拖拽等操作。比如下面的過程:
- 將平面繞平面的Y軸旋轉一定角度
- 將其繞平面自身的X軸旋轉一定角度
- 將平面向向量方向(1, 2, 3)移動一段距離。
這樣複雜的操作通常需要我們使用vtkActor提供的操作函式:
void vtkProp3D::RotateY (double angle) void vtkProp3D::RotateX (double angle) void vtkProp3D::AddPosition (double deltaX,double deltaY,double deltaZ)
控制運動的介面我們找到了,但是傳入的引數還需要認真思考如何計算。
通常,我們是在事件中計算位置移動向量,或者旋轉角度,這裡就需要用到vtkRenderWindowInteractor提供的GetEventPosition和GetLastEventPosition函式。他們幫助我們計算Display座標系的游標座標值。
平移向量是向量 GetEventPosition => GetLastEventPosition 在平面法向量上的投影。
旋轉角度則是 planeOrigin => GetLastEventPosition 與 planeOrigin => GetEventPosition的向量夾角。
如此可知,每一次運動,都需要知道平面中心點或者平面的法向量。同時,在一次運動結束之後,平面中心點或者平面法向量都應該更新。
在文章
由actor matrix構造出來的vtkTransform物件不要在求解origin之後又立馬求解normal。
#include <stdio.h> #include <vtkSmartPointer.h> #include <vtkSphereSource.h> #include <vtkActor.h> #include <vtkPlaneSource.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkPolyDataMapper.h> #include <vtkProperty.h> #include <vtkRenderWindowInteractor.h> #include <vtkLight.h> #include <vtkCamera.h> #include <vtkActor2D.h> #include <vtkTransform.h> using namespace std; int main() { setbuf( stdout, NULL ); vtkSmartPointer<vtkPlaneSource> plane = vtkSmartPointer<vtkPlaneSource>::New(); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection( plane->GetOutputPort() ); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper( mapper ); double oldNormal[3] = { 0, 0, -1 }; double oldOrigin[3] = { 0, 0, 0 }; actor->RotateY( 45 ); // actor->AddPosition( 1, -1, 1 ); vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New(); transform->SetMatrix( actor->GetMatrix() ); double *newNormal = transform->TransformDoubleVector( oldNormal ); vtkSmartPointer<vtkTransform> transform2 = vtkSmartPointer<vtkTransform>::New(); transform2->SetMatrix( actor->GetMatrix() ); double *newOrigin = transform2->TransformDoublePoint( oldOrigin ); printf( "newNormal: %lf, %lf, %lf\n", newNormal[0], newNormal[1], newNormal[2] ); printf( "newOrigin: %lf, %lf, %lf\n", newOrigin[0], newOrigin[1], newOrigin[2] ); /* newOrigin: 0.000000, 0.000000, 0.000000 newNormal: -0.707107, 0.000000, -0.707107 (-cos45) */ vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->SetBackground( 0, 0, 0 ); vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer( renderer ); vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); renderWindowInteractor->SetRenderWindow( renderWindow ); renderWindowInteractor->GetLastEventPosition(); renderer->ResetCamera(); renderWindow->Render(); renderWindowInteractor->Start(); return 0; }
比如上面例子中的double *newOrigin = transform2->TransformDoublePoint( oldOrigin );
如果我是繼續使用了transform,那麼origin與normal的數值就是一樣的了。先計算誰誰就錯了。
錯誤結果:
newNormal: 0.000000, 0.000000, 0.000000
newOrigin: 0.000000, 0.000000, 0.000000
這是為什麼呢?
看看兩個函式的實現:
double *TransformDoublePoint(double x, double y, double z) {
this->InternalDoublePoint[0] = x;
this->InternalDoublePoint[1] = y;
this->InternalDoublePoint[2] = z;
this->TransformPoint(this->InternalDoublePoint,this->InternalDoublePoint);
return this->InternalDoublePoint; };
//------------------------------------------------------------------------
double *TransformDoubleVector(double x, double y, double z) {
this->InternalDoublePoint[0] = x;
this->InternalDoublePoint[1] = y;
this->InternalDoublePoint[2] = z;
this->TransformVector(this->InternalDoublePoint,this->InternalDoublePoint);
return this->InternalDoublePoint; };
vtkLinearTransform將計算結果全都儲存在InternalDoublePoint中了,所以例子中的newOrigin和newNormal最終指向同一個double陣列,這個陣列儲存著最後一次計算的結果,即newOrigin。