開發環境:
- Windows 11 家庭中文版
- Microsoft Visual Studio Community 2019
- VTK-9.3.0.rc0
- vtk-example
- 參考代碼
demo解決問題:允許用戶使用三維光標在三維空間中定位一個點。關鍵類
vtkPointWidget
, 光標具有輪廓邊界框、軸對齊十字準線和軸陰影(輪廓和陰影可以關閉)。(可以關閉輪廓和陰影)。vtkPointWidget 和其他 3D widget 一樣,具有一個很好的特點,即它可以與當前的交互樣式一起工作。也就是說,如果 vtkPointWidget 沒有處理事件,那么所有其他已注冊的觀察者(包括交互樣式)都有機會處理該事件。否則,vtkPointWidget 將終止處理它所處理的事件。
主流程:(不看probe)
- 數據源1:構造一個網格化的sphereSource數據源
- 數據源2:point的位置使用cone符號化為圓錐體
- 數據源3:添加一個AddActor2D,固定在視口左下角
- 數據源4:構造3D控件pointWidget,并添加觀察者myCallback,監控pointWidget交互事件
注意:point符號化的過程中,一開始是沒有符號的,所以圓錐體一開始不顯示,交互時間開始后設置了point的值,點背符號化后有了圓錐體,Execute中關鍵代碼:
//獲取定義該點的多邊形數據(包括點)。單個點和一個頂點組成 vtkPolyData。
pointWidget->GetPolyData(this->PolyData);//給this->PolyData / point 賦值,在多個管道中間中途修改值,修改后update修改過的管道,render
this->PositionActor->SetInput(text.str().c_str());
glyph的輸入我把probefilter刪了,直接用point數據,也是可以相同效果的,目前沒有明白為什么要加一個vtkProbeFilter,理解的幫解答下,謝謝拉!!!
另一個需要重點關注的是需要區分以下接口接口
vtkNew<vtkProbeFilter> probe;//指定一個數據對象作為輸入。請注意,此方法不會建立管道連接。使用 SetInputConnection() 來 建立管道連接。probe->SetInputData(point);//輸入: 此時的point值為空,需要事件內根據鼠標位置進行賦值//指定將在輸入點進行探測的數據集。 //輸入為輸出提供幾何圖形(點和單元)、 而源點則通過探測(插值)生成標量、 矢量等。probe->SetSourceData(inputPolyData);//源:
參考鏈接1
參考鏈接2
參考鏈接3
vtkNew<vtkGlyph3D> glyph;// glyph->SetInputConnection(probe->GetOutputPort());//???不理解glyph->SetInputData(point); //此處直接使用point也可以達到效果,但是為什么非要用vtkProbeFilter 沒有想通glyph->SetSourceConnection(cone->GetOutputPort());
參考鏈接1
參考鏈接2
prj name: Arbitrary3DCursor
#include <vtkActor.h>
#include <vtkCallbackCommand.h>
#include <vtkCommand.h>
#include <vtkConeSource.h>
#include <vtkGlyph3D.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPointWidget.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProbeFilter.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkTextActor.h>
#include <vtkTextProperty.h>
#include <vtkXMLPolyDataReader.h>#include <iostream>
#include <sstream>
#include <string>// This does the actual work: updates the probe.
// Callback for the interaction.
class vtkmyPWCallback : public vtkCallbackCommand
{
public:vtkmyPWCallback() = default;static vtkmyPWCallback* New(){return new vtkmyPWCallback;}virtual void Execute(vtkObject* caller, unsigned long, void*){vtkPointWidget* pointWidget = reinterpret_cast<vtkPointWidget*>(caller);//獲取定義該點的多邊形數據(包括點)。單個點和一個頂點組成 vtkPolyData。pointWidget->GetPolyData(this->PolyData);//給this->PolyData / point 賦值,在多個管道中間中途修改值,修改后update修改過的管道,renderdouble position[3];pointWidget->GetPosition(position);std::ostringstream text;text << "cursor: " << std::fixed << std::setprecision(4) << position[0]<< ", " << position[1] << ", " << position[2];this->PositionActor->SetInput(text.str().c_str());// this->CursorActor->VisibilityOn();std::cout << PolyData->GetNumberOfCells() << std::endl;std::cout << PolyData->GetNumberOfPoints() << std::endl;std::cout << PolyData->GetNumberOfPolys() << std::endl;}vtkPolyData* PolyData = nullptr; //與傳入的錐形有關//vtkActor* CursorActor = nullptr; //可以不需要,如果需要控制顯示隱藏狀態,可以傳入vtkTextActor* PositionActor = nullptr;//實時顯示坐標狀態
};int main(int argc, char* argv[])
{vtkSmartPointer<vtkPolyData> inputPolyData;if (argc > 1){vtkNew<vtkXMLPolyDataReader> reader;reader->SetFileName(argv[1]);reader->Update();inputPolyData = reader->GetOutput();}else{vtkNew<vtkSphereSource> sphereSource;sphereSource->SetPhiResolution(15);sphereSource->SetThetaResolution(15);sphereSource->Update();inputPolyData = sphereSource->GetOutput();}vtkNew<vtkNamedColors> colors;vtkNew<vtkPolyData> point;//https://blog.csdn.net/liushao1031177/article/details/122860254//https://blog.csdn.net/yuyangyg/article/details/78165570//https://www.cnblogs.com/ankier/p/3166210.html/*在指定點位置采樣數據值vtkProbeFilter 是一個過濾器,用于計算指定點位置的點屬性(如標量、矢量等)。該過濾器有兩個輸入:輸入和源。輸入的幾何結構通過過濾器。通過對源數據進行插值,在輸入點位置計算出點屬性。例如,我們可以根據體積(源數據)計算平面(指定為輸入的平面)上的數據值。源數據的單元格數據會根據每個輸入點所在的源單元格復制到輸出端。如果源點數據和單元格數據中都存在同名數組,則只探查點數據中的數組。該過濾器可用于重新采樣數據,或將一種數據集形式轉換為另一種數據集形式。例如,非結構化網格(vtkUnstructuredGrid)可以用體積(三維 vtkImageData)進行探測,然后使用體積渲染技術將結果可視化。另一個例子:可以使用一條直線或曲線來探測數據,以生成沿該直線或曲線的 x-y 圖。警告vtkProbeFilter 的一個關鍵算法組件是其查找包含探測點的單元格的方式。默認情況下,vtkDataSet::FindCell() 方法會被使用,該方法反過來使用 vtkPointLocator 來執行加速搜索。不過,在某些情況下,使用 vtkPointLocator 可能無法識別包圍單元格。更穩健但更慢的方法是使用 vtkCellLocator 執行 FindCell() 操作(通過指定 CellLocatorPrototype)。最后,可以通過指定 vtkFindCellStrategy 的實例來配置更高級的搜索。(注意:圖像數據探測從不使用定位器,因為查找包含的單元格是一個簡單、快速的操作。因此指定 vtkFindCellStrategy 或單元格定位器原型沒有任何作用)。vtkProbeFilter 一旦找到包含查詢點的單元格,就會使用單元格的插值函數來執行插值/計算點屬性。vtkPointInterpolator 支持多種廣義內核,而 vtkSPHInterpolator 則支持多種 SPH 內核。*/vtkNew<vtkProbeFilter> probe;//指定一個數據對象作為輸入。請注意,此方法不會建立管道連接。使用 SetInputConnection() 來 建立管道連接。probe->SetInputData(point);//輸入: 此時的point值為空,需要事件內根據鼠標位置進行賦值//指定將在輸入點進行探測的數據集。 //輸入為輸出提供幾何圖形(點和單元)、 而源點則通過探測(插值)生成標量、 矢量等。probe->SetSourceData(inputPolyData);//源: std::cout << point->GetNumberOfCells() << std::endl;std::cout << point->GetNumberOfPoints() << std::endl;std::cout << point->GetNumberOfPolys() << std::endl;// Create glyph.vtkNew<vtkConeSource> cone;cone->SetResolution(30);//https://blog.csdn.net/jigetage/article/details/86633156//https://www.cnblogs.com/vaughnhuang/p/17584058.htmlvtkNew<vtkGlyph3D> glyph;// glyph->SetInputConnection(probe->GetOutputPort());//???不理解glyph->SetInputData(point); //此處直接使用point也可以達到效果,但是為什么非要用vtkProbeFilter 沒有想通glyph->SetSourceConnection(cone->GetOutputPort());glyph->SetVectorModeToUseVector();glyph->SetScaleModeToDataScalingOff();glyph->SetScaleFactor(inputPolyData->GetLength() * 0.1);vtkNew<vtkPolyDataMapper> glyphMapper;glyphMapper->SetInputConnection(glyph->GetOutputPort());vtkNew<vtkActor> glyphActor;glyphActor->SetMapper(glyphMapper);glyphActor->VisibilityOn();//point為空,沒有glyph顯示vtkNew<vtkPolyDataMapper> mapper;mapper->SetInputData(inputPolyData);vtkNew<vtkActor> actor;actor->SetMapper(mapper);actor->GetProperty()->SetRepresentationToWireframe();actor->GetProperty()->SetColor(colors->GetColor3d("gold").GetData());vtkNew<vtkTextActor> textActor;textActor->GetTextProperty()->SetFontSize(12);textActor->SetPosition(10, 20);textActor->SetInput("cursor:");textActor->GetTextProperty()->SetColor(colors->GetColor3d("White").GetData());// Create the RenderWindow, Render1er and both Actors.vtkNew<vtkRenderer> ren1;vtkNew<vtkRenderWindow> renWin;renWin->AddRenderer(ren1);vtkNew<vtkRenderWindowInteractor> iren;iren->SetRenderWindow(renWin);// The SetInteractor method is how 3D widgets are associated with the render// window interactor. Internally, SetInteractor sets up a bunch of callbacks// using the Command/Observer mechanism (AddObserver()).vtkNew<vtkmyPWCallback> myCallback;myCallback->PolyData = point;//myCallback->PolyData = inputPolyData;//myCallback->CursorActor = glyphActor;myCallback->PositionActor = textActor;// The point widget is used probe the dataset.vtkNew<vtkPointWidget> pointWidget;pointWidget->SetInteractor(iren);pointWidget->SetInputData(inputPolyData);//指定移動范圍pointWidget->AllOff();pointWidget->PlaceWidget();pointWidget->AddObserver(vtkCommand::InteractionEvent, myCallback);ren1->AddActor(glyphActor);ren1->AddActor(actor);ren1->AddActor2D(textActor);//2D actor// Add the actors to the renderer, set the background and size.ren1->GradientBackgroundOn();ren1->SetBackground(colors->GetColor3d("SlateGray").GetData());ren1->SetBackground2(colors->GetColor3d("Wheat").GetData());renWin->SetSize(300, 300);renWin->SetWindowName("Arbitrary3DCursor");renWin->Render();pointWidget->On();// Render the imageiren->Initialize();renWin->Render();iren->Start();return EXIT_SUCCESS;
}