日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

VTK可视化管线

發布時間:2025/3/15 编程问答 68 豆豆
生活随笔 收集整理的這篇文章主要介紹了 VTK可视化管线 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


4、VTK可視化管線

通過第3章的學習,我們已經了解了VTK的一些基礎概念。在這一章里,我們將更深入地學習VTK,其中包括VTK的系統框架結構、引用計數、智能指針、Observer/Command設計機制以及本章的重點內容——VTK可視化管線結構。通過本章的學習,可能你對VTK的設計框架將會有更深一層的理解。

所謂追根溯源,首先我們先了解一下VTK里絕大多數類的共同的父類vtkObjectBase和vtkObject。

4.1 vtkObjectBase和vtkObject

vtkObjectBase是一個抽象基類,派生出絕大多數的VTK類。它是VTK里所有引用計數(Reference Counting)類的基類,著名的子類包括:vtkCommand,vtkInformationKey和vtkObject。

4.1.1 引用計數

如果很多對象有相同的值,將這個值存儲多次是很無聊的。更好的辦法是讓所有的對象共享這個值的實現。這么做不但節省內存,而且可以使得程序運行更快,因為不需要構造和析構這個值的拷貝。引用計數就是這樣一個技巧,它允許多個有相同值的對象共享這個值的實現。引用計數是個簡單的垃圾回收體系,只要其它對象引用某對象(記為對象O),對象O就會存在一個引用計數,當最后引用對象O的對象移除,O對象就會自動析構。VTK里使用引用計數的好處是,可以實現數據之間的共享而不用拷貝,從而達到節省內存的目的。

我們可以看一個簡單的例子(4.1.1_ReferenceCounting):

?

///ReferenceCount.cpp/

??1:? #include"vtkSmartPointer.h"

??2:? #include"vtkBMPReader.h"

??3:? #include"vtkImageData.h"

??4:??

??5:? int main(int argc, char*argv[])

??6:? {

??7:? vtkSmartPointer<vtkBMPReader>reader = vtkSmartPointer<vtkBMPReader>::New();

??8:???reader->SetFileName("../test.bmp");

??9:??? reader->Update();

?10:??

?11:???std::cout<<"Reference Count of reader->GetOutput (BeforeAssignment) = "

?12:???????<<reader->GetOutput()->GetReferenceCount()<<std::endl;

?13:??

?14:??? vtkSmartPointer<vtkImageData> image1 = reader->GetOutput();

?15:???std::cout<<"Reference Count of reader->GetOutput (Assignto image1) = "

?16:???????<<reader->GetOutput()->GetReferenceCount()<<std::endl;

?17:???std::cout<<"Reference Count of image1 = "

?18:???????<<image1->GetReferenceCount()<<std::endl;

?19:??

?20:???vtkSmartPointer<vtkImageData> image2 = reader->GetOutput();

?21:???std::cout<<"Reference Count of reader->GetOutput (Assignto image2) = "

?22:???????<<reader->GetOutput()->GetReferenceCount()<<std::endl;

?23:???std::cout<<"Reference Count of image2 = "

?24:???????<<image2->GetReferenceCount()<<std::endl;

?25:??

?26:??? return 0;

?27:? }

//

?

程序輸出結果如圖4.1所示。


圖4.1ReferenceCount運行結果

在ReferenceCount示例里,我們先用vtkBMPReader讀入一幅BMP圖像test.bmp,在賦值之前我們輸出了reader->GetOutput()的引用計數值,其值為1(使用方法New()創建對象以后,初始的引用計數值就等于1);然后我們創建了一個vtkImageData類型的對象image1,并把reader的輸出賦給了image1,這時image1就指向了reader的輸出,也就是說,reader的輸出多了一個引用,這個時候輸出的reader->GetOutput()和image1的引用計數都為2;接著我們又創建一個類型同樣為vtkImageData的對象image2,同樣也是把reader的輸出賦值給image2,這時,image2也指向reader的輸出,亦即reader的輸出又多了一個引用,所以輸出的reader->GetOutput()和image2的引用計數值變成了3。image1和image2的數據結構可以簡單地描述為圖4.2。


圖4.2image1,image2,reader->GetOutput()及引用計數之間的結構關系

一旦某個對象的引用計數等于0時,就表明沒有別的對象再引用它,它的使命也宣告完成,程序就會自動的析構這個對象。在ReferenceCount這個示例里,我們看不到引用計數減少的相關代碼,這是因為我們使用了智能指針vtkSmartPointer。

4.1.2 智能指針

智能指針會自動管理引用計數的增加與減少,如果檢測到某對象的引用計數值減少為0,則會自動地釋放該對象的資源,從而達到自動管理內存的目的。

在前面的內容我們已經介紹過,VTK里,要創建一個對象可以用兩種方法,一種是使用vtkObjectBase里的靜態成員變量New(),用Delete()方法析構;另一種就是我們示例里使用多次的使用智能指針vtkSmartPointer<T>。

對于第一種方法,用New()創建的對象,程序最后必須要調用Delete()方法釋放對應的內存,而且由于vtkObjectBase及其子類的構造函數都是聲明為受保護的,這意味著它們不能在棧區(棧區上的內存是由編譯器自動分配與釋放的,堆區上的內存則是由程序員分配和手動釋放的。)上分配內存。比如:

vtkBMPReader*reader = vtkBMPReader::New(); //創建vtkBMPReader對象

……

reader->Delete();//程序最后要調用Delete(),這里并沒有直接析構對象,而是使引用計數值減1。

用New()創建的對象,如果沒有用Delete()方法刪除的話,程序有可能會出現內存泄漏,即用戶負責對象內存的管理。

如果使用智能指針創建的對象,則無需手動調用Delete()方法讓引用計數減少,因為引用計數的增加與減少都是由智能指針自動完成的。使用智能指針時,首先是要包含智能指針的頭文件:#include "vtkSmartPointer.h"。vtkSmartPointer是一個模板類,所需的模板參數就是待創建的對象的類名,如:

vtkSmartPointer<vtkImageData>image = vtkSmartPointer< vtkImageData >::New();

注意上面一行代碼等號右邊寫法,不能寫為:

vtkSmartPointer<vtkImageData > image = vtkImageData::New();

也就是不能把對象的原始指針賦給智能指針,上行代碼編譯的時候可以通過,但程序退出時會有內存泄漏,就是因為智能指針無法自動釋放該對象的內存,如圖4.3所示。


圖4.3 將對象的原始指針賦予智能指針會引起內存泄漏

如果沒有給對象分配內存,仍然可以使用智能指針,比如:

vtkSmartPointer<vtkBMPReader>reader =vtkSmartPointer<vtkBMPReader>::New();

vtkImageData* imageData=reader->GetOutput();

或者:

vtkSmartPointer< vtkImageData> imageData = reader->GetOutput();

第一種情況,當reader超出其作用域時,數據即會被刪除;第二種情況,使用了智能指針,所以數據的引用計數會自動加1,除非reader和imageData都超出它們的作用域,數據才會被刪除。

智能指針類型同樣也可以作為函數的返回值。正確的寫法類似:

vtkSmartPointer<vtkImageData>MyFunction()

{

? vtkSmartPointer<vtkImageData> myObject= vtkSmartPointer<vtkImageData>::New();

? return myObject;

}

調用時則是:

vtkSmartPointer<vtkImageData>MyImageData = MyFunction();

函數MyFunction()的返回值是通過拷貝的方式,將數據賦予調用的變量,因此該數據的引用計數保持不變,而且函數MyFunction里的myObject對象也不會刪除。

下面的函數形式和函數調用是錯誤的,應該引起注意:

vtkImageData* MyFunction()

{

? vtkSmartPointer< vtkImageData >MyObject = vtkSmartPointer< vtkImageData >::New();

? return MyObject;

}

vtkImageData* MyImageData = MyFunction();

在函數MyFunction()里定義的是智能指針類型的,最后返回時轉換成原始指針類型,當函數調用結束時,智能指針的引用計數會減為0,即函數MyFunction里的MyObject對象會被刪除掉,也就是說MyFunction()返回的是懸空指針,這時再賦予MyImageData變量就會出錯。

智能指針類型也可以作為類的成員變量,而且會使得類在析構時更加容易,不用人為去做任何釋放內存的事情,把這些工作都交給智能指針來完成,例如:

classMyClass

{

? vtkSmartPointer<vtkFloatArray>Distances;

};

然后在類的構造函數里進行初始化:

MyClass::MyClass()

{

? Distances = vtkSmartPointer<vtkFloatArray>::New();

}

在類的析構函數里不用調用Delete()去刪除任何東西。

智能指針有一個讓人困惑的地方:當你創建一個智能指針類型的對象,然后改變它的指向,這時引用計數就會出錯。例如:

vtkSmartPointer<vtkImageData>imageData = vtkSmartPointer<vtkImageData>::New();

imageData= Reader->GetOutput();

上面兩行代碼里,我們首先創建一個imageData,并給他分配好了內存,接著我們又把imageData指向Reader的輸出,而不是一直指向我們創建的那塊內存。對于這種情況,我們只要簡單地調用:

vtkImageData*imageData = Reader->GetOutput();

這里沒有必要使用智能指針,因為我們沒有實際創建任何新的對象。

綜上所述,可以看出引用計數和智能指針是息息相關的,它們主要都是用于內存管理。使用智能指針可以免去很多手動刪除變量的煩惱,所以在本教程里,我們從一開始都使用智能指針來創建VTK對象。如果你想了解更多關于引用計數和智能指針的內容,可以參考C++的經典著作《More Effective C++》這本書。

4.1.3 運行時類型識別 (Run-Time Type Information,RTTI)

在C++里,對象類型是通過typeid (需要包含頭文件#include<type_info>)獲取的;VTK里在vtkObjectBase定義了獲取對象類型的方法:GetClassName()和IsA()。GetClassName()返回的是該對象類名的字符串(VTK用類名來識別各個對象),如:

vtkSmartPointer<vtkBMPReader>Reader = vtkSmartPointer<vtkBMPReader>::New();

constchar* type = Reader->GetClassName(); //返回“vtkBMPReader”字符串

IsA()方法用于測試某個對象是否為指定字符串的類型或其子類,比如:

if(Reader->IsA(“vtkImageReader”) ) {……}; // 這里IsA()會返回真。

類比C++里的操作RTTI操作符,除了typeid之外,還有dynamic_cast,主要用于基類向子類的類型轉換,稱為向下轉型。VTK里同樣提供了類似的方法,也就是vtkObject里定義的SafeDownCast(),它是vtkObject里的靜態成員函數,意味著它是屬于類的,而不是屬于對象的,即可以用vtkObject::SafeDownCast()直接調用,比如:

vtkSmartPointer<vtkImageReader>ReaderBase = vtkSmartPointer<vtkImageReader>::New();

vtkBMPReader*bmpReader = vtkBMPReader::SafeDownCast(ReaderBase);

與dynamic_cast類似,SafeDownCast也是運行時才轉換的,這種轉換只有當bmpReader的類型確實是ReaderBase的派生類時才有效,否則返回空指針。

除了運行時類型識別,vtkObjectBase還提供了用于調試的狀態輸出接口Print()。雖然vtkObjectBase里除了Print()還提供PrintSelf()、PrintHeader()、PrintTrailer()等公共接口,但在調試VTK程序時,如果需要輸出某個對象的狀態信息時,一般都是調用Print()函數,如:

bmpReader->Print(std::cout);

4.1.4 關于vtkObject的兩三事

以上的幾小節都是在討論vtkObjectBase這個VTK始祖類的一些特性,接下來我們看一下vtkObjectBase這個始祖類其中一個“兒子”的本領。vtkObject是大多數VTK類的父類,是一個抽象基類,大多數在VTK框架里的類都應該是它的子類或其某個子類的子類。這一小節我們起名為“關于vtkObject的兩三事”,因為vtkObject剛好做了三件重要的事情。

第一件事是,vtkObject里定義了與程序調試相關的一些公共接口,包括:

?

DebugOn() / DebugOff()

GetDebug() / SetDebug(unsignedchar)

?

SetGlobalWarningDisplay(int) / GetGlobalWarningDisplay()

GlobalWarningDisplayOn() / GlobalWarningDisplayOff()

?

其中后四個是靜態成員函數。我們可以通過示例(4.1.4_vtkObjectDemo)來看看vtkObject做的第一件事到底是什么。

?

///vtkObjectDemo.cpp/

??1:? #include"vtkSmartPointer.h"

??2:? #include"vtkBMPReader.h"

??3:? #include "vtkImageViewer2.h"

??4:? #include"vtkRenderWindowInteractor.h"

??5:??

??6:? int main(int argc, char*argv[])

??7:? {

??8:???vtkSmartPointer<vtkBMPReader> reader =vtkSmartPointer<vtkBMPReader>::New();

??9:???reader->SetFileName("../monochrome.bmp");

??10:???reader->Allow8BitBMPOn();

?11:??? reader->SetDebug(1);

?12:??? //reader->SetDebug(0);

?13:??? //reader->GlobalWarningDisplayOff();

?14:??? reader->Update();

?15:??

?16:???vtkSmartPointer<vtkImageViewer2> viewer =vtkSmartPointer<vtkImageViewer2>::New();

?17:???viewer->SetInput(reader->GetOutput());

?18:???

?19:???vtkSmartPointer<vtkRenderWindowInteractor> interactor =

?20:???????vtkSmartPointer<vtkRenderWindowInteractor>::New();

?21:???viewer->SetupInteractor(interactor);

?22:?? ?viewer->Render();

?23:??

?24:???interactor->Initialize();

?25:??? interactor->Start();

?26:??

?27:??? return 0;

28:? }

//

第11行SetDebug(1)作用等同于DebugOn(),第12行SetDebug(0)等同于DebugOff()。示例里我們讀入的BMP圖像是單色的,由于VTK不支持單色的BMP圖像的讀取,所以如果調用方法SetDebug(1)或者DebugOn()時,則會彈出圖4.4-A所示的窗口;調用DebugOff()時,彈出的窗口如圖4.4-B所示,如果不想看到vtkOutputWindow窗口,可以調用GlobalWarningDisplayOff()或者SetGlobalWarningDisplay(0)。


圖4.4vtkOutputWindow窗口

因為VTK不支持1位BMP圖像的讀取,所以調用reader->GetOutput()時不能得到正確的輸出,最終會導致程序在關閉時,出現內存溢出的錯誤,如圖4.5所示。


圖4.5 示例4.1.4_vtkObjectDemo程序退出時內存溢出錯誤

:示例4.1.4_vtkObjectDemo也可以作一下更改,以便讓它支持命令行參數,作為BMP圖像的瀏覽器,修改后的工程為BMPImageViewer。比如在CMD窗口里輸入命令:

D:\Toolkits\VTK\Examples\4.1.4_vtkObjectDemo\bin\Debug\BMPImageViewer.exeD:\Toolkits\VTK\Examples\4.1.4_vtkObjectDemo\test.bmp (回車,即可運行程序)

或者可以直接在VS2008里輸出需要的命令行參數。右擊“BMPImageViewer”工程,選擇屬性,然后選擇屬性對話框左邊的Debugging,接著在“Command Arguments”一欄輸出所需的參數,比如..\test.bmp (可以用絕對路徑,如果路徑里含有空格,記得用雙引號把整個路徑括起來)。確定,保存所做的更改。最后在VS2008下F5運行程序。

如果你感覺程序運行時后面老是跟著控制臺窗口,讓你覺得不爽的話,你可以在main()函數之前加入下面的語句隱藏控制臺窗口,一般建議在發布程序之前不要隱藏,方便調試程序時隨時輸出調試信息。

#pragmacomment(linker,"/subsystem:\"windows\"/entry:\"mainCRTStartup\"")

vtkObject的第二件事是,實現觀察者/命令(Observer/Command)設計模式。本教程不是專門介紹設計模式的,如果你想更深入地了解Observer/Command設計模式,可以翻翻相關的書籍,下面只是就這兩種設計模式蜻蜓點水般的做一概述。

vtkObject定義了與觀察者Observer相關的方法(如,AddObserver()/RemoveObserver()),觀察者模式主要針對兩個對象:Object和Observer。一個Object可以有多個Observer,它定義對象間的一種一對多的依賴關系,當—個Object對象的狀態發生改變時,所有依賴于它的Observer對象都得到通知被自動更新。

命令模式屬于對象行為模式,它將—個請求封裝為一個對象,并提供一致性發送請求的接口,當一個事件發生時,它不直接把事件傳遞到事件調用者,而是在命令和調用者之間增加—個中間者,將這種直接關系切斷,同時兩者之間都隔離。事件調用者只是和接口打交道,不和具體實現交互。命令模式的實現是由vtkObjectBase的另外一個重要子類vtkCommand及其派生類實現的。

如果你還是覺得抽象的話,我們就看看下面的示例(ObserverCommandDemo.cpp):

?

ObserverCommandDemo.cpp///

??1:? #include"vtkSmartPointer.h"

??2:? #include"vtkBMPReader.h"

??3:? #include"vtkImageViewer2.h"

??4:? #include"vtkRenderWindowInteractor.h"

??5:? #include"vtkCallbackCommand.h"

??6:??

??7:? long pressCounts = 0;

??8:??

??9:? //第一步,定義回調函數。

?10:? //注意回調函數的簽名,不能更改。

?11:? void MyCallbackFunc(vtkObject*, unsigned long eid, void* clientdata,void *calldata)

?12:? {

?13:?????std::cout<<"You have clicked:"<<++pressCounts<<" times."<<std::endl;

?14:? }

?15:??

?16:? int main(int argc, char*argv[])

?17:? {

?18:???vtkSmartPointer<vtkBMPReader> reader =

?19:???????vtkSmartPointer<vtkBMPReader>::New();

?20:???reader->SetFileName("../test.bmp");

?21:???reader->Allow8BitBMPOn();

?22:??? reader->SetDebug(0);

?23:???reader->GlobalWarningDisplayOff();

?24:??? reader->Update();

?25:??

?26:???vtkSmartPointer<vtkImageViewer2> viewer =

?27:?????? ?vtkSmartPointer<vtkImageViewer2>::New();

?28:???viewer->SetInput(reader->GetOutput());

?29:???

?30:???vtkSmartPointer<vtkRenderWindowInteractor> interactor =

?31:???????vtkSmartPointer<vtkRenderWindowInteractor>::New();

?32:??? viewer->SetupInteractor(interactor);

?33:??? viewer->Render();

?34:??

?35:??? //第二步,設置回調函數。

?36:??? vtkSmartPointer<vtkCallbackCommand> mouseCallback =

?37:???????vtkSmartPointer<vtkCallbackCommand>::New();

?38:??? mouseCallback->SetCallback ( MyCallbackFunc );

?39:??

?40:??? //第三步,將vtkCallbackCommand對象添加到觀察者列表。

?41:???interactor->SetRenderWindow(viewer->GetRenderWindow());

?42:??? interactor->AddObserver(vtkCommand::LeftButtonPressEvent,mouseCallback);

?43:??

?44:???interactor->Initialize();

?45:??? interactor->Start();

?46:??

?47:??? return 0;

?48:? }

//

?

示例ObserverCommandDemo運行結果如圖4.6所示。


圖4.6 示例ObserverCommandDemo運行結果

從示例ObserverCommandDemo可以看到,VTK里使用事件回調函數時,需要分三步走。首先,定義回調函數?;卣{函數的簽名只能是以下形式:

void MyCallbackFunc(vtkObject*obj,unsigned long eid, void* clientdata, void *calldata)

其次是創建一個vtkCallbackCommand對象,并調用vtkCallbackCommand::SetCallback()設置第一步定義的回調函數。

最后是將vtkCallbackCommand對象添加到對象的觀察者列表。VTK里,所有的事件都是通過vtkInteractorObserver(vtkRenderWindowInteractor的父類,vtkInteractorObserver派生自vtkObject) 進行監聽的,所以,我們把vtkCallbackCommand對象加入到vtkRenderWindowInteractor對象的觀察者列表中,調用的就是vtkObject提供的公共接口AddObserver(),其原型為:

unsigned longAddObserver (unsigned long event, vtkCommand *, float priority=0.0f)

第一個參數是要監聽的事件,這些事件定義在vtkCommand類里,如:

RenderEvent /ProgressEvent / PickEvent / StartPickEvent / EndPickEvent / ExitEvent /LeftButtonPressEvent / LeftButtonReleaseEvent / MiddleButtonPressEvent /MiddleButtonReleaseEvent / RightButtonPressEvent / RightButtonReleaseEvent /KeyPressEvent / KeyReleaseEvent???? /CharEvent / TimerEvent

示例中我們監聽的是鼠標左鍵的單擊事件,即LeftButtonPressEvent。

AddObserver的第二個參數是vtkCommand類型的指針,即我們創建的已經綁定回調函數的vtkCallbackCommand對象。第三個參數是設置命令響應的優先權。

AddObserver函數的返回值是unsigned long型的,可以用于把觀察者從觀察者列表中刪除RemoveObserver(eventID)。

示例ObserverCommandDemo監聽交互過程中的鼠標左鍵單擊事件,如果監聽到該事件,就在控制臺中輸出目前為止鼠標的單擊次數,該示例僅僅是為了演示觀察者/命令模式的工作方式,除此之外沒有任何實用價值。除了用以上介紹的回調函數形式來完成事件/回調的工作,同樣也可以用類(派生自vtkCommand)的形式來完成。示例ObserverCommandDemo2里,我們會看到稍微復雜一點的應用。

?

ObserverCommandDemo2.cpp/

??1:? #include"vtkSmartPointer.h"

??2:? #include"vtkConeSource.h"

??3:? #include"vtkPolyDataMapper.h"

??4:? #include"vtkRenderWindow.h"

??5:? #include"vtkRenderWindowInteractor.h"

??6:? #include"vtkCamera.h"

??7:? #include"vtkActor.h"

??8:? #include"vtkRenderer.h"

??9:? #include"vtkCommand.h"

?10:? #include"vtkBoxWidget.h"

?11:? #include"vtkTransform.h"

?12:? #include"vtkInteractorStyleTrackballCamera.h"

?13:??

?14:? //第一步

?15:? class vtkMyCallback : publicvtkCommand

?16:? {

?17:? public:

?18:??? static vtkMyCallback *New()

?19:????? { return newvtkMyCallback; }

?20:??

?21:??? virtual voidExecute(vtkObject *caller, unsigned long eventId, void* callData)

?22:????? {

?23:??????? vtkTransform *t =vtkTransform::New();

?24:??????? vtkBoxWidget *widget =reinterpret_cast<vtkBoxWidget*>(caller);

?25:???????widget->GetTransform(t);

?26:???????widget->GetProp3D()->SetUserTransform(t);

?27:??????? t->Delete();

?28:????? }

?29:? };

?30:??

?31:? int main()

?32:? {

?33:???vtkSmartPointer<vtkConeSource> cone =vtkSmartPointer<vtkConeSource>::New();

?34:??? cone->SetHeight( 3.0 );

?35:??? cone->SetRadius( 1.0 );

?36:??? cone->SetResolution( 10);

?37:??

?38:???vtkSmartPointer<vtkPolyDataMapper> coneMapper =

?39:???????vtkSmartPointer<vtkPolyDataMapper>::New();

?40:???coneMapper->SetInputConnection( cone->GetOutputPort() );

?41:??

?42:???vtkSmartPointer<vtkActor> coneActor = vtkSmartPointer<vtkActor>::New();

?43:??? coneActor->SetMapper(coneMapper );

?44:??

?45:???vtkSmartPointer<vtkRenderer> ren1=vtkSmartPointer<vtkRenderer>::New();

?46:??? ren1->AddActor(coneActor );

?47:??? ren1->SetBackground(0.1, 0.2, 0.4 );

?48:??

?49:???vtkSmartPointer<vtkRenderWindow> renWin =

?50:???????vtkSmartPointer<vtkRenderWindow>::New();

?51:??? renWin->AddRenderer(ren1 );

?52:??? renWin->SetSize( 300,300 );

?53:??

?54:???vtkSmartPointer<vtkRenderWindowInteractor> iren =

?55:???? ???vtkSmartPointer<vtkRenderWindowInteractor>::New();

?56:???iren->SetRenderWindow(renWin);

?57:??

?58:???vtkSmartPointer<vtkInteractorStyleTrackballCamera> style =

?59:?????vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();

?60:??? iren->SetInteractorStyle(style);

?61:??

?62:??? //通過vtkBoxWidget可以控制coneActor的變換矩陣,從而實現coneActor的形變

?63:???vtkSmartPointer<vtkBoxWidget> boxWidget =vtkSmartPointer<vtkBoxWidget>::New();

?64:???boxWidget->SetInteractor(iren);

?65:??? boxWidget->SetPlaceFactor(1.25);

?66:???boxWidget->SetProp3D(coneActor);

?67:???boxWidget->PlaceWidget();

?68:??

?69:??? //第二步

?70:???vtkSmartPointer<vtkMyCallback> callback =vtkSmartPointer<vtkMyCallback>::New();

?71:??

?72:??? //第三步

?73:??? boxWidget->AddObserver(vtkCommand::InteractionEvent,callback);

?74:??

?75:??? //激活Widget。按“i”鍵可以關閉或激活Widget。

?76:??? boxWidget->On();

?77:??

?78:??? iren->Initialize();

?79:??? iren->Start();

?80:??

?81:??? return 0;

?82:? }

//

?

示例的第31行一直到61行,都是我們比較熟悉的代碼,唯一比較陌生的類是vtkBoxWidget,你可以先查看一下這個類的說明文檔,關于Widget的使用后續章節也會重點介紹,這里暫且不提,我們的重點是Observer/Command的應用。

與回調函數的類似,首先我們從vtkCommand派生出類vtkMyCallback,該類主要實現兩個方法,一個是New(),用于為創建的對象申請內存;一個是Execute(),這是父類vtkCommand里定義的純虛函數,其原型為:

virtual voidExecute(vtkObject *caller, unsigned long eventId,void *callData) = 0;

這意味著,只要從vtkCommand派生的類,都必須實現這個方法。而這個方法的作用就是一旦監聽到所要監聽的事件,就會自動地調用該方法。監聽到事件后,要完成什么樣的操作,都是在Execute()方法里實現,所以我們把精力都放在這個方法上。第一個參數是caller,指向調用觀察者的對象,即調用AddObserver()方法的那個對象,如本例中的boxWidget。第二個參數eventId是事件的編號。第三個參數callData,是傳遞給Execute函數的數據,本例我們沒有給該函數傳遞任何數據,如果你很想知道這個參數有值時應該如何使用,可以找找你計算機上的VTK目錄(D:\Toolkits\VTK\VTK-5.10\Examples\Tutorial\Step2\Cxx\Cone2.cxx)。

將Execute()與前面介紹的回調函數作一對比:

void MyCallbackFunc(vtkObject*obj,unsigned long eid, void* clientdata, void *calldata)

前兩個參數都是一樣的,MyCallbackFunc的第四個參數與Execute()的第三個參數一樣,第三個參數是clientdata,這個數據是指回調函數里需要訪問主程序里的數據時,由主程序向回調函數傳遞的,可以通過方法vtkCallbackCommand::SetClientData()設置。

接下來我們看看MyCallback::Execute()的實現(23到27行)。首先我們定義一個vtkTransform對象;然后把傳遞過來的數據caller用C++操作符reinterpret_cast<>轉換成類型vtkBoxWidget*,示例里的這個轉換是可以成功的,因為調用觀察者的對象也是vtkBoxWidget*類型的(73行)。緊接著,我們把從vtkBoxWidget對象獲取到的變換重新應用到vtkBoxWidget里的Prop對象,也就是說Prop對象會跟著vtkBoxWidget對象做同樣的變換,或伸或縮。

第二步(69-70行),實例化一個vtkMyCallback對象。

第三步(72-73行),監聽vtkBoxWidget對象的交互事件(InteractionEvent)。也就是說,當用戶與vtkBoxWidget對象交互時,事件InteractionEvent就會被觸發,程序就會自動調用vtkMyCallback里的Execute()事件。

示例ObserverCommandDemo2運行結果如圖4.7。

圖4.7 ObserverCommandDemo2運行結果(按“i”鍵可以關閉或激活Widget)

Observer/Command模式在VTK里的應用是非常廣泛也是非常重要的,可以用回調函數或者類的形式來實現,以上的內容已經給出比較詳細的介紹,使用VTK的話,這兩種方式是沒有理由不掌握的。

我們繼續看vtkObject的第三件事。翻翻vtkObject.h文件,里面有一個受保護的數據成員MTime,與這個MTime相關的公共接口有GetMTime() (返回MTime的值)以及Modified()。MTime全稱就是Modification Time,也就是修改時間。vtkObject的第三件事就是我們接下來要學習的內容——“可視化管線”。


==========歡迎轉載,轉載時請保留該聲明信息==========

版權歸@東靈工作室所有,更多信息請訪問東靈工作室


教程系列導航:http://blog.csdn.net/www_doling_net/article/details/8763686

================================================


總結

以上是生活随笔為你收集整理的VTK可视化管线的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。