前にやったときはコールバック関数を直接呼び出していたが今度はクラスのインスタンスを作成してExecuteメンバを呼び出す。
VTKのタイマーは一つのコールバック用のクラスインスタンスのExecuteを各タイミングで呼び出す。
タイマーの停止はDestroyTimerで行う。
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> //円筒とその表示に必要 #include <vtkCylinderSource.h> #include <vtkPolyDataMapper.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle);
class MyTimerCallback : public vtkCommand { public: static MyTimerCallback* New() { return new MyTimerCallback; } int counter100 = 0; int counter500 = 0; void Execute(vtkObject* caller, unsigned long eventId, void* callData) override { if (eventId == vtkCommand::TimerEvent) { int timerId = *(int*)(callData); if (timerId == this->TimerId100) { // TimerId100の処理 counter100++; std::cout << "Timer100 triggered!" << counter100 <<std::endl; // タイマーを停止 if(counter100 >= 10) { std::cout << "Stopping Timer100" << std::endl; Interactor->DestroyTimer(timerId); } } else if (timerId == this->TimerId500) { // TimerId500の処理 counter500++; std::cout << "Timer500 triggered!" << counter500 << std::endl; } } } vtkSmartPointer<vtkRenderWindowInteractor> Interactor; int TimerId100 = -1; // タイマーの識別子 int TimerId500 = -1; };
int main(int /*argc*/, char** /*argv*/) { ////////////////////////////////////// // Create a vtkCylinderSource vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New(); cylinderSource->SetCenter(0.0, 0.0, 0.0); cylinderSource->SetRadius(5.0); cylinderSource->SetHeight(7.0); cylinderSource->SetResolution(100); ////////////////////////////////////// // Create a mapper and actor vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cylinderSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); ////////////////////////////////////// auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->ResetCamera(); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render(); ////////////////////////////////////// // タイマーコールバックの設定 vtkSmartPointer<MyTimerCallback> timerCallback = vtkSmartPointer<MyTimerCallback>::New(); // タイマーを作成。戻り値は識別子 timerCallback->TimerId100 = interactor->CreateRepeatingTimer(100); timerCallback->TimerId500 = interactor->CreateRepeatingTimer(500); // DestroyTimerのためにinteractorを保持 timerCallback->Interactor = interactor; interactor->AddObserver(vtkCommand::TimerEvent, timerCallback); interactor->SetRenderWindow(renderer->GetRenderWindow()); interactor->Initialize(); ////////////////////////////////////// interactor->Start(); //イベントループへ入る return 0; }
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> //円筒とその表示に必要 #include <vtkCylinderSource.h> #include <vtkPolyDataMapper.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle); #include <vtkInteractorStyleTrackballCamera.h> #include <vtkCamera.h> #include <vtkPropPicker.h> #include <vtkAppendPolyData.h> #include <vtkLineSource.h> #include <vtkProperty.h> // 注視点を表示するためのアクタを作成 vtkSmartPointer<vtkActor> Create3DPlusActor( double length = 1.0, double width = 2.0 );
// マウスイベントをカスタマイズするためのクラス class MouseInteractorStyle2 : public vtkInteractorStyleTrackballCamera { public: // 注視点を表示するためのアクタ vtkSmartPointer<vtkActor> focal_mark; static MouseInteractorStyle2* New(); vtkTypeMacro(MouseInteractorStyle2, vtkInteractorStyleTrackballCamera); virtual void OnLeftButtonDown() { int ctrl = this->Interactor->GetControlKey(); if (ctrl) { int* clickPos = this->GetInteractor()->GetEventPosition(); // ピック下オブジェクトを取得 auto picker = vtkSmartPointer<vtkPropPicker>::New(); picker->Pick(clickPos[0], clickPos[1], 0, this->GetDefaultRenderer()); // オブジェクトがクリックされていたら位置とアドレスを出力 if (picker->GetActor() != nullptr) { double* pos = picker->GetPickPosition(); auto camera = this->GetDefaultRenderer()->GetActiveCamera(); camera->SetFocalPoint(pos); // カメラの焦点をクリック位置に設定 focal_mark->SetPosition(pos); // クリック位置に移動 } } // Forward events vtkInteractorStyleTrackballCamera::OnLeftButtonDown(); } private: };
vtkStandardNewMacro(MouseInteractorStyle2); int main(int /*argc*/, char** /*argv*/) { ////////////////////////////////////// // Create a vtkCylinderSource vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New(); cylinderSource->SetCenter(0.0, 0.0, 0.0); cylinderSource->SetRadius(5.0); cylinderSource->SetHeight(7.0); cylinderSource->SetResolution(100); ////////////////////////////////////// // Create a mapper and actor vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cylinderSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); ////////////////////////////////////// auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->ResetCamera(); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render();
////////////////////////////////////// // マウスイベントを設定. auto style = vtkSmartPointer<MouseInteractorStyle2>::New(); style->SetDefaultRenderer(renderer); interactor->SetInteractorStyle(style); ////////////////////////////////////// vtkCamera* cam = renderer->GetActiveCamera(); double focal[3]; cam->GetFocalPoint(focal); auto axes = Create3DPlusActor(1, 1); axes->SetPosition(focal); renderer->AddActor(axes); // 軸をレンダラーに追加 style->focal_mark = axes;
////////////////////////////////////// interactor->Start(); //イベントループへ入る return 0; } // 線分の長さ、太さ、色を指定できる vtkSmartPointer<vtkActor> Create3DPlusActor( double length, double width ) { double center[3]= { 0.0, 0.0, 0.0 }; double color[3] = { 1.0, 1.0, 1.0 }; // 各軸の線分を作成 auto appendFilter = vtkSmartPointer<vtkAppendPolyData>::New(); // x軸の線 auto xLine = vtkSmartPointer<vtkLineSource>::New(); xLine->SetPoint1(center[0] - length / 2.0, center[1], center[2]); xLine->SetPoint2(center[0] + length / 2.0, center[1], center[2]); xLine->Update(); appendFilter->AddInputData(xLine->GetOutput()); // y軸の線 auto yLine = vtkSmartPointer<vtkLineSource>::New(); yLine->SetPoint1(center[0], center[1] - length / 2.0, center[2]); yLine->SetPoint2(center[0], center[1] + length / 2.0, center[2]); yLine->Update(); appendFilter->AddInputData(yLine->GetOutput()); // z軸の線 auto zLine = vtkSmartPointer<vtkLineSource>::New(); zLine->SetPoint1(center[0], center[1], center[2] - length / 2.0); zLine->SetPoint2(center[0], center[1], center[2] + length / 2.0); zLine->Update(); appendFilter->AddInputData(zLine->GetOutput()); appendFilter->Update(); // マッパーとアクター auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(appendFilter->GetOutputPort()); auto actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); actor->GetProperty()->SetLineWidth(width); actor->GetProperty()->SetColor(color); return actor; }
昔書いた内容かどうか思い出せない。コードを書いた記憶はあるが記事が見つからなかった。
やったことはあるかもしれないが、きっと当時よりはよいコードになっている。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string> // ウィンドウ作成 class MyFrame : public wxFrame { public: void PostCreate() { // ウィンドウサイズ this->SetSize(wxSize(200, 200)); // ウィンドウのサイズを設定 this->Layout(); // レイアウトの更新 } void func1(); void func2(); void func3(); void func4(); void func5(); void func6(); void func7(); void func8(); void func9(); void func10(); MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); //func1(); //func2(); //func3(); func4(); //func5(); //func6(); //func7(); //func8(); //func9(); //func10(); } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
// 全体に広げる void MyFrame::func1() { wxButton* mybutton = new wxButton( this, wxID_ANY, "button", wxDefaultPosition, wxSize(100, 100), wxTAB_TRAVERSAL | wxBORDER_SIMPLE ); // 縦方向のBoxSizer wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); // ウィジェットをSizerに追加 // wxEXPANDでフルサイズに広げる(1=比率) sizer->Add(mybutton, 1, wxEXPAND | wxALL, 0); // パネルにSizerを適用 this->SetSizer(sizer); }
// 上下に平等 void MyFrame::func2() { wxButton* mybutton1 = new wxButton( this, wxID_ANY, "1", wxDefaultPosition, wxSize(100, 100), wxTAB_TRAVERSAL | wxBORDER_SIMPLE ); mybutton1->SetMinSize(wxSize(20, 20)); wxButton* mybutton2 = new wxButton( this, wxID_ANY, "2", wxDefaultPosition, wxSize(100, 100), wxTAB_TRAVERSAL | wxBORDER_SIMPLE ); mybutton2->SetMinSize(wxSize(20, 20)); // 上下に並べて表示 wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); // ウィジェットをSizerに追加 // wxEXPANDでフルサイズに広げる(1=比率) sizer->Add(mybutton1, 1, wxEXPAND | wxALL, 0); sizer->Add(mybutton2, 1, wxEXPAND | wxALL, 0); // パネルにSizerを適用 this->SetSizer(sizer); // レイアウトの更新 this->Layout(); }
// 左右に平等 void MyFrame::func3() { wxButton* mybutton1 = new wxButton( this, wxID_ANY, "1", wxDefaultPosition, wxSize(100, 100), wxTAB_TRAVERSAL | wxBORDER_SIMPLE ); mybutton1->SetMinSize(wxSize(20, 20)); wxButton* mybutton2 = new wxButton( this, wxID_ANY, "2", wxDefaultPosition, wxSize(100, 100), wxTAB_TRAVERSAL | wxBORDER_SIMPLE ); mybutton2->SetMinSize(wxSize(20, 20)); // 左右に並べて表示 wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); // ウィジェットをSizerに追加 // wxEXPANDでフルサイズに広げる(1=比率) sizer->Add(mybutton1, 1, wxEXPAND | wxALL, 0); sizer->Add(mybutton2, 1, wxEXPAND | wxALL, 0); // パネルにSizerを適用 this->SetSizer(sizer); // レイアウトの更新 this->Layout(); }
// 画面中央 固定サイズ void MyFrame::func4() { wxButton* mybutton1 = new wxButton( this, wxID_ANY, "1", wxDefaultPosition, wxSize(100, 100), // ボタンのサイズを指定 wxTAB_TRAVERSAL | wxBORDER_SIMPLE ); wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(0, 0, 1, wxEXPAND, 0); // ダミー。proportion=1で「残りスペースを埋める」設定 sizer->Add(mybutton1, 0, wxALIGN_CENTER | wxALL, 0); // proportion=0 で固定サイズ sizer->Add(0, 0, 1, wxEXPAND, 0); // ダミー。proportion=1で「残りスペースを埋める」設定 this->SetSizer(sizer); }
// 画面四分割 wxGridSizer void MyFrame::func5() { // 2x2のGridSizerを作成(行数:2, 列数:2, 両マージン:0) wxGridSizer* sizer = new wxGridSizer(2, 2, 0, 0); // 4つのボタンを作成して追加 wxButton* button1 = new wxButton(this, wxID_ANY, "1"); wxButton* button2 = new wxButton(this, wxID_ANY, "2"); wxButton* button3 = new wxButton(this, wxID_ANY, "3"); wxButton* button4 = new wxButton(this, wxID_ANY, "4"); // 各ボタンをSizerに追加(比率1、フル展開) sizer->Add(button1, 1, wxEXPAND | wxALL, 0); sizer->Add(button2, 1, wxEXPAND | wxALL, 0); sizer->Add(button3, 1, wxEXPAND | wxALL, 0); sizer->Add(button4, 1, wxEXPAND | wxALL, 0); this->SetSizer(sizer); this->Layout(); }
// 画面四分割 wxSizer void MyFrame::func6() { wxButton* button1 = new wxButton(this, wxID_ANY, "1"); wxButton* button2 = new wxButton(this, wxID_ANY, "2"); wxButton* button3 = new wxButton(this, wxID_ANY, "3"); wxButton* button4 = new wxButton(this, wxID_ANY, "4"); // 大きな縦方向Sizer(2行) wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); // 1行目 wxBoxSizer* row1 = new wxBoxSizer(wxHORIZONTAL); row1->Add(button1, 1, wxEXPAND | wxALL, 0); row1->Add(button2, 1, wxEXPAND | wxALL, 0); mainSizer->Add(row1, 1, wxEXPAND | wxALL, 0); // 2行目 wxBoxSizer* row2 = new wxBoxSizer(wxHORIZONTAL); row2->Add(button3, 1, wxEXPAND | wxALL, 0); row2->Add(button4, 1, wxEXPAND | wxALL, 0); mainSizer->Add(row2, 1, wxEXPAND | wxALL, 0); // フレームにSizerを適用 this->SetSizer(mainSizer); this->Layout(); }
// 左側幅固定 void MyFrame::func7() { wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); // 左のボタン(幅50px固定) wxButton* leftButton = new wxButton(this, wxID_ANY, "Left"); leftButton->SetMinSize(wxSize(50, -1));// 幅50px, 高さは自動調整 leftButton->SetMaxSize(wxSize(50, -1)); // 右のボタン(残りスペースを埋める) wxButton* rightButton = new wxButton(this, wxID_ANY, "Right"); // Sizerに追加 sizer->Add(leftButton, 0, wxEXPAND | wxALL, 0); // 右は proportion=0 で固定サイズ sizer->Add(rightButton, 1, wxEXPAND | wxALL, 0);// 左は proportion=1 で残りを埋める // フレームに適用 this->SetSizer(sizer); this->Layout(); }
// 右側幅固定 void MyFrame::func8() { wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); // 左のボタン(残りスペースを埋める) wxButton* leftButton = new wxButton(this, wxID_ANY, "Left"); // 右のボタン(幅500px固定) wxButton* rightButton = new wxButton(this, wxID_ANY, "Right"); // 幅を固定するための設定 rightButton->SetMinSize(wxSize(50, -1)); rightButton->SetMaxSize(wxSize(50, -1)); // Sizerに追加 sizer->Add(leftButton, 1, wxEXPAND | wxALL, 0); // 左は proportion=1 で残りを埋める sizer->Add(rightButton, 0, wxEXPAND | wxALL, 0); // 右は proportion=0 で固定サイズ // フレームに適用 this->SetSizer(sizer); this->Layout(); }
// 上側高さ固定 void MyFrame::func9() { wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); // 上のボタン(高さ固定) wxButton* topButton = new wxButton(this, wxID_ANY, "Top"); topButton->SetMinSize(wxSize(-1, 50)); // 高さ100pxを保証 topButton->SetMaxSize(wxSize(-1, 50)); // 高さ100pxに固定 // 下のボタン(残りスペースを埋める) wxButton* bottomButton = new wxButton(this, wxID_ANY, "Bottom"); // Sizerに追加 sizer->Add(topButton, 0, wxEXPAND | wxALL, 0); // proportion=0 で固定サイズ sizer->Add(bottomButton, 1, wxEXPAND | wxALL, 0); // proportion=1 で残りスペース // フレームに適用 this->SetSizer(sizer); this->Layout(); }
// 下側高さ固定 void MyFrame::func10() { wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); // 上のボタン wxButton* topButton = new wxButton(this, wxID_ANY, "Top"); // 下のボタン wxButton* bottomButton = new wxButton(this, wxID_ANY, "Bottom"); // 高さを固定するための設定 bottomButton->SetMinSize(wxSize(-1, 50)); bottomButton->SetMaxSize(wxSize(-1, 50)); // Sizerに追加 sizer->Add(topButton, 1, wxEXPAND | wxALL, 0); // proportion=1 で残りスペース sizer->Add(bottomButton, 0, wxEXPAND | wxALL, 0); // proportion=0 で固定サイズ // フレームに適用 this->SetSizer(sizer); this->Layout(); }
カメラ状態を保存リストアする。行列ではないので各ベクトルや値を個別に取り出さないといけない。
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> //円筒とその表示に必要 #include <vtkCylinderSource.h> #include <vtkPolyDataMapper.h> #include <vtkCamera.h> #include <vtkCommand.h> #include <vtkRendererCollection.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle);
struct CameraState { double Position[3]; double FocalPoint[3]; double ViewUp[3]; double ClippingRange[2]; double ViewAngle; };
// カメラの状態を保存する関数 CameraState GetCameraState(vtkCamera* camera) { CameraState state; camera->GetPosition(state.Position); camera->GetFocalPoint(state.FocalPoint); camera->GetViewUp(state.ViewUp); camera->GetClippingRange(state.ClippingRange); state.ViewAngle = camera->GetViewAngle(); return state; }
// カメラの状態を復元する関数 void LoadCameraState(vtkCamera* camera, const CameraState& state) { camera->SetPosition(state.Position); camera->SetFocalPoint(state.FocalPoint); camera->SetViewUp(state.ViewUp); camera->SetClippingRange(state.ClippingRange); camera->SetViewAngle(state.ViewAngle); }
// キーボード入力を処理 class MyCommand : public vtkCommand { CameraState camera_state; public: static MyCommand* New() { return new MyCommand; } void SetMapper(vtkSmartPointer<vtkPolyDataMapper> mapper) { this->Mapper = mapper; } void Execute(vtkObject* caller, unsigned long eventId, void*) override { if (eventId == vtkCommand::KeyPressEvent) { vtkRenderWindowInteractor* interactor = dynamic_cast<vtkRenderWindowInteractor*>(caller); std::string key = interactor->GetKeySym(); // 注意 デフォルト機能で E キーで終了してしまう // キー入力を取得 if (key == "s") {
// カメラの状態を保存 vtkRenderer* renderer = interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer(); vtkCamera* camera = renderer->GetActiveCamera(); // cameraをcamera_stateに保存 camera_state = GetCameraState(camera);
} else if (key == "r") {
// カメラの状態を復元 vtkRenderer* renderer = interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer(); vtkCamera* camera = renderer->GetActiveCamera(); // camera_stateをcameraに復元 LoadCameraState(camera, camera_state);
} interactor->GetRenderWindow()->Render(); } } private: vtkSmartPointer<vtkPolyDataMapper> Mapper; }; int main(int /*argc*/, char** /*argv*/) { ////////////////////////////////////// // Create a vtkCylinderSource vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New(); cylinderSource->SetCenter(0.0, 0.0, 0.0); cylinderSource->SetRadius(5.0); cylinderSource->SetHeight(7.0); cylinderSource->SetResolution(100); ////////////////////////////////////// // Create a mapper and actor vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(cylinderSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); ////////////////////////////////////// auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->ResetCamera(); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); // キー入力で色を切り替えるコマンドを設定 vtkSmartPointer<MyCommand> MyKeyboardAction = vtkSmartPointer<MyCommand>::New(); MyKeyboardAction->SetMapper(mapper); interactor->AddObserver(vtkCommand::KeyPressEvent, MyKeyboardAction); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render(); interactor->Start(); //イベントループへ入る return 0; }
以前書いた奴と同じだが自分で読んでわかりにくかったので書き直し。
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkDoubleArray.h> #include <vtkPointData.h> #include <vtkActor.h> #include <vtkCellArray.h> #include <vtkPoints.h> #include <vtkPolyData.h> #include <vtkPolyDataMapper.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"psapi.lib") #pragma comment(lib,"dbghelp.lib") #pragma comment(lib,"ws2_32.lib") //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle);
void create( vtkSmartPointer<vtkPoints> points, vtkSmartPointer<vtkDoubleArray> colors, vtkSmartPointer<vtkCellArray> cells ) { for (size_t i = 0; i < 100000; i++) { double x = (double)rand() / (double)RAND_MAX; double y = (double)rand() / (double)RAND_MAX; double z = (double)rand() / (double)RAND_MAX; points->InsertNextPoint(x, y, z); // 座標追加 colors->InsertNextTuple3(x, y, z); // 色情報追加 cells->InsertNextCell(1); // 要素数 cells->InsertCellPoint(i); // 頂点インデックスを追加 } }
vtkSmartPointer<vtkActor> createActor() { // 頂点配列 vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); // 色情報の配列 vtkSmartPointer<vtkDoubleArray> colors = vtkSmartPointer<vtkDoubleArray>::New(); colors->SetNumberOfComponents(3); colors->SetName("Colors"); // 頂点インデクスの配列 vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New(); create(points, colors, cells); /////////////////////////////////////////////////// // --- vtkPolyData作成 --- auto polydata = vtkSmartPointer<vtkPolyData>::New(); polydata->SetPoints(points); polydata->SetVerts(cells); polydata->GetPointData()->SetScalars(colors); // --- actor作成 --- auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputData(polydata); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); return actor; }
int main(int /*argc*/, char** /*argv*/) { auto actor = createActor(); auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->ResetCamera(); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render(); interactor->Start(); //イベントループへ入る return 0; }
Open3DのOBB。GetBoxPoints()のコメントに各頂点のindexが載っているのでそれを参考にすればLineSetで表示できる。
#include <Windows.h> #include <open3d/Open3D.h> #include <GL/GL.h> #pragma comment(lib, "opengl32.lib") std::shared_ptr<open3d::geometry::LineSet> obb_to_lineset(const open3d::geometry::OrientedBoundingBox& obb) { // BoundingVolume.h 参照 /// 0 ------------------- 1 /// /| /| /// / | / | /// / | / | /// / | / | /// 2 ------------------- 7 | /// | |____________|____| 6 /// | /3 | / /// | / | / /// | / | / /// |/ |/ /// 5 ------------------- 4 std::vector<Eigen::Vector3d> points = obb.GetBoxPoints(); std::vector<Eigen::Vector2i> linesegments; // 0-2-7-1 linesegments.push_back(Eigen::Vector2i(0,2)); linesegments.push_back(Eigen::Vector2i(2,7)); linesegments.push_back(Eigen::Vector2i(7,1)); linesegments.push_back(Eigen::Vector2i(1,0)); // 4-5-3-6 linesegments.push_back(Eigen::Vector2i(4,5)); linesegments.push_back(Eigen::Vector2i(5,3)); linesegments.push_back(Eigen::Vector2i(3,6)); linesegments.push_back(Eigen::Vector2i(6,4)); // 0-3-5-2 linesegments.push_back(Eigen::Vector2i(0,3)); linesegments.push_back(Eigen::Vector2i(3,5)); linesegments.push_back(Eigen::Vector2i(5,2)); linesegments.push_back(Eigen::Vector2i(2,0)); // 4-6-1-7 linesegments.push_back(Eigen::Vector2i(4,6)); linesegments.push_back(Eigen::Vector2i(6,1)); linesegments.push_back(Eigen::Vector2i(1,7)); linesegments.push_back(Eigen::Vector2i(7,4)); // 色を設定 std::vector<Eigen::Vector3d> colors; for (size_t i = 0; i < linesegments.size(); ++i) { colors.push_back(Eigen::Vector3d(0.0, 0.0, 0.0)); } auto line_set = std::make_shared<open3d::geometry::LineSet>(); line_set->points_ = points; line_set->lines_ = linesegments; line_set->colors_ = colors; return line_set; } int main() { // 点群データの作成 auto cloud = std::make_shared<open3d::geometry::PointCloud>(); // ランダムな点を生成 for (int i = 0; i < 50000; ++i) { double x = (static_cast<double>(rand()) / RAND_MAX) * 80.0; double y = (static_cast<double>(rand()) / RAND_MAX) * 30.0; double z = (static_cast<double>(rand()) / RAND_MAX) * 10.0; cloud->points_.push_back(Eigen::Vector3d(x, y, z)); } auto lineset = obb_to_lineset(cloud->GetOrientedBoundingBox()); // 表示用のウィンドウを作成 open3d::visualization::Visualizer visualizer; visualizer.CreateVisualizerWindow("Open3D Visualization", 800, 600); visualizer.AddGeometry(cloud); visualizer.AddGeometry(lineset); visualizer.UpdateGeometry(); visualizer.PollEvents(); visualizer.UpdateRender(); // ウィンドウを閉じるまでループ while (visualizer.PollEvents()) { visualizer.UpdateRender(); } visualizer.DestroyVisualizerWindow(); return 0; }
VTM、VTPはXML形式なので、VTKを使わなくても出力できる。自分で作ったデータをvtm,vtpで出力するとparaview等で可視化できる。
XMLなのでpugixmlを使用。
save_vtm_vtkfreeで.vtmファイルを保存する。
#pragma once #include <Eigen/Dense> #include <filesystem> #include <vector> #include <string> #include "pugixml.hpp" struct Data3D { std::vector< Eigen::Vector3d > points; std::vector< Eigen::Vector3i > triangles; // 頂点ID三つで構成される三角形 std::vector< int > polyline;// N個の頂点を格納した頂点リストで表現される折れ線 };
void save_vtp_vtkfree(const std::string& filename, const Data3D& data) { pugi::xml_document doc; auto vtkFile = doc.append_child("VTKFile"); vtkFile.append_attribute("type") = "PolyData"; vtkFile.append_attribute("version") = "1.0"; vtkFile.append_attribute("byte_order") = "LittleEndian"; auto polyData = vtkFile.append_child("PolyData"); polyData.append_attribute("WholeExtent") = "0 0 0 0 0 0"; auto piece = polyData.append_child("Piece"); piece.append_attribute("NumberOfPoints") = data.points.size(); piece.append_attribute("NumberOfLines") = data.polyline.empty() ? 0 : 1; piece.append_attribute("NumberOfPolys") = data.triangles.size(); ////////////////////////////////////////////////////////////////// // Points auto pointsNode = piece.append_child("Points"); auto pointsArray = pointsNode.append_child("DataArray"); pointsArray.append_attribute("type") = "Float64"; pointsArray.append_attribute("NumberOfComponents") = 3; pointsArray.append_attribute("format") = "ascii"; std::ostringstream pointsStream; for (const auto& p : data.points) { pointsStream << p.x() << " " << p.y() << " " << p.z() << " "; } pointsArray.text().set(pointsStream.str().c_str()); ////////////////////////////////////////////////////////////////// // PolyLine if (!data.polyline.empty()) { auto linesNode = piece.append_child("Lines"); auto connArray = linesNode.append_child("DataArray"); connArray.append_attribute("type") = "Int32"; connArray.append_attribute("Name") = "connectivity"; connArray.append_attribute("format") = "ascii"; std::ostringstream connStream; for (const auto& pointIndex : data.polyline) { connStream << pointIndex << " "; } connArray.text().set(connStream.str().c_str()); auto offsetArray = linesNode.append_child("DataArray"); offsetArray.append_attribute("type") = "Int32"; offsetArray.append_attribute("Name") = "offsets"; offsetArray.append_attribute("format") = "ascii"; std::ostringstream offsetStream; offsetStream << data.polyline.size(); offsetArray.text().set(offsetStream.str().c_str()); } ////////////////////////////////////////////////////////////////// // Triangles if (!data.triangles.empty()) { auto polysNode = piece.append_child("Polys"); auto polyConnArray = polysNode.append_child("DataArray"); polyConnArray.append_attribute("type") = "Int32"; polyConnArray.append_attribute("Name") = "connectivity"; polyConnArray.append_attribute("format") = "ascii"; std::ostringstream polyConnStream; for (const auto& tri : data.triangles) { polyConnStream << tri[0] << " " << tri[1] << " " << tri[2] << " "; } polyConnArray.text().set(polyConnStream.str().c_str()); auto polyOffsetArray = polysNode.append_child("DataArray"); polyOffsetArray.append_attribute("type") = "Int32"; polyOffsetArray.append_attribute("Name") = "offsets"; polyOffsetArray.append_attribute("format") = "ascii"; std::ostringstream polyOffsetStream; for (size_t i = 0; i < data.triangles.size(); ++i) { polyOffsetStream << (i + 1) * 3 << " "; } polyOffsetArray.text().set(polyOffsetStream.str().c_str()); } ////////////////////////////////////////////////////////////////// doc.save_file(filename.c_str()); }
// VTKの形式であるVTMファイルを保存する関数 // ただしVTKライブラリを使用しない void save_vtm_vtkfree(const std::string& filename, const std::vector<Data3D>& dataList) { // filenameからディレクトリ名を取得 std::filesystem::path path(filename); std::string directory = path.parent_path().string(); std::string filenameWithoutExtension = path.stem().string(); if( directory.empty() ) { directory = "."; } std::string vtpdirectory = directory + "/" + filenameWithoutExtension; // ディレクトリが存在しない場合は作成存在する場合は中を空にする if (std::filesystem::exists(vtpdirectory)) { for (const auto& entry : std::filesystem::directory_iterator(vtpdirectory)) { std::filesystem::remove_all(entry.path()); } } std::filesystem::create_directories(vtpdirectory); pugi::xml_document doc; auto vtkFile = doc.append_child("VTKFile"); vtkFile.append_attribute("type") = "vtkMultiBlockDataSet"; vtkFile.append_attribute("version") = "1.0"; vtkFile.append_attribute("byte_order") = "LittleEndian"; auto mbNode = vtkFile.append_child("vtkMultiBlockDataSet"); for (size_t i = 0; i < dataList.size(); ++i) { std::string vtpFilename = filenameWithoutExtension + "_" + std::to_string(i) + ".vtp"; save_vtp_vtkfree(vtpdirectory + "/" + vtpFilename, dataList[i]); auto dataSetNode = mbNode.append_child("DataSet"); dataSetNode.append_attribute("index") = static_cast<int>(i); dataSetNode.append_attribute("file") = (filenameWithoutExtension + "/" + vtpFilename).c_str(); } doc.save_file(filename.c_str()); }
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkActor.h> #include <vtkPolyData.h> #include <vtkPolyDataMapper.h> #include <vtkCellArray.h> #include <vtkPoints.h> #include <vtkLine.h> #include <vtkTriangle.h> #include <vtkProperty.h> #include <vtkXMLPolyDataReader.h> #include <vtkXMLMultiBlockDataReader.h> #include <vtkMultiBlockDataSet.h> //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle); #include "save_vtm_vtkfree.hpp" // メッシュデータの生成関数 Data3D createSampleData(); void displayVTP(const std::string& filename, bool showLines, bool showPolys, double lineWidth, double lineColor[3], double polyColor[3]); int main(int /*argc*/, char** /*argv*/) { Data3D data = createSampleData(); Data3D data2 = createSampleData(); for(auto& p : data2.points){ p.x() += 1.0; p.y() += 1.0; p.z() += 1.0; } //データをVTK形式で保存 std::vector<Data3D> dataList; dataList.push_back(data); dataList.push_back(data2); save_vtm_vtkfree("C:\\test\\test2.vtm", dataList); double c[3] = { 1.0, 0.0, 0.0 }, d[3] = { 0.0, 1.0, 0.0 }; displayVTP("C:\\test\\test2.vtm", true, true, 5.0, c,d); return 0; } Data3D createSampleData() { Data3D data; // 点群の生成 data.points.push_back(Eigen::Vector3d(0.0, 0.0, 0.0)); data.points.push_back(Eigen::Vector3d(1.0, 0.0, 0.0)); data.points.push_back(Eigen::Vector3d(1.0, 1.0, 0.0)); data.points.push_back(Eigen::Vector3d(0.0, 1.0, 0.0)); data.points.push_back(Eigen::Vector3d(0.5, 0.5, 1.0)); // 三角形の生成 (底面と頂点を結ぶ三角形) data.triangles.push_back(Eigen::Vector3i(0, 1, 4)); data.triangles.push_back(Eigen::Vector3i(1, 2, 4)); data.triangles.push_back(Eigen::Vector3i(2, 3, 4)); data.triangles.push_back(Eigen::Vector3i(3, 0, 4)); data.triangles.push_back(Eigen::Vector3i(0, 1, 2)); data.triangles.push_back(Eigen::Vector3i(0, 2, 3)); // 折れ線の生成 data.polyline = { 0, 1, 2, 3, 0, }; return data; } void displayVTP(const std::string& filename, bool showLines, bool showPolys, double lineWidth, double lineColor[3], double polyColor[3]) { vtkSmartPointer<vtkXMLMultiBlockDataReader> reader = vtkSmartPointer<vtkXMLMultiBlockDataReader>::New(); reader->SetFileName(filename.c_str()); reader->Update(); vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->SetBackground(0.1, 0.1, 0.1); vtkMultiBlockDataSet* multiBlockData = vtkMultiBlockDataSet::SafeDownCast(reader->GetOutput()); if (!multiBlockData) return; for (unsigned int i = 0; i < multiBlockData->GetNumberOfBlocks(); ++i) { vtkSmartPointer<vtkPolyData> polyData = vtkPolyData::SafeDownCast(multiBlockData->GetBlock(i)); if (!polyData) continue; if (showLines && polyData->GetLines()->GetNumberOfCells() > 0) { vtkSmartPointer<vtkPolyDataMapper> lineMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); lineMapper->SetInputData(polyData); vtkSmartPointer<vtkActor> lineActor = vtkSmartPointer<vtkActor>::New(); lineActor->SetMapper(lineMapper); lineActor->GetProperty()->SetColor(lineColor); lineActor->GetProperty()->SetLineWidth(lineWidth); renderer->AddActor(lineActor); } if (showPolys && polyData->GetPolys()->GetNumberOfCells() > 0) { vtkSmartPointer<vtkPolyDataMapper> polyMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); polyMapper->SetInputData(polyData); vtkSmartPointer<vtkActor> polyActor = vtkSmartPointer<vtkActor>::New(); polyActor->SetMapper(polyMapper); polyActor->GetProperty()->SetColor(polyColor); polyActor->GetProperty()->SetEdgeVisibility(false); renderer->AddActor(polyActor); } } vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); interactor->SetRenderWindow(renderWindow); renderWindow->Render(); interactor->Start(); }
test2.vtm
<?xml version="1.0"?> <VTKFile type="vtkMultiBlockDataSet" version="1.0" byte_order="LittleEndian"> <vtkMultiBlockDataSet> <DataSet index="0" file="test2/test2_0.vtp" /> <DataSet index="1" file="test2/test2_1.vtp" /> </vtkMultiBlockDataSet> </VTKFile>
test2/test2_0.vtp
<?xml version="1.0"?> <VTKFile type="PolyData" version="1.0" byte_order="LittleEndian"> <PolyData WholeExtent="0 0 0 0 0 0"> <Piece NumberOfPoints="5" NumberOfLines="1" NumberOfPolys="6"> <Points> <DataArray type="Float64" NumberOfComponents="3" format="ascii">0 0 0 1 0 0 1 1 0 0 1 0 0.5 0.5 1 </DataArray> </Points> <Lines> <DataArray type="Int32" Name="connectivity" format="ascii">0 1 2 3 0 </DataArray> <DataArray type="Int32" Name="offsets" format="ascii">5</DataArray> </Lines> <Polys> <DataArray type="Int32" Name="connectivity" format="ascii">0 1 4 1 2 4 2 3 4 3 0 4 0 1 2 0 2 3 </DataArray> <DataArray type="Int32" Name="offsets" format="ascii">3 6 9 12 15 18 </DataArray> </Polys> </Piece> </PolyData> </VTKFile>
何度か作っているが少し真面目に作った。wxSizer等は使っていない。
#ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #include "MyItemPanel.hpp" //! @brief 自作リストコントロール class MyObjectListWidget : public wxPanel { wxPanel* _viewArea; //!< リスト表示エリア wxScrollBar* _scrollBar; //!< スクロールバー int _itemHeight; //!< 一つのアイテムの高さ //! @brief 作成したパネルのポインタ一覧 std::vector< ItemPanel* > _createdPanels; //! @brief パネルを生成するオブジェクト std::shared_ptr<MyObjectListItemAccessor> _itemaccessor; int _activedItemIndex; //!< 現在アクティブなアイテムのインデックス //!< パネルを表示 void SetItemPanels(); //!< ウィジェット生成直後の動作 void PostCreate(); public: MyObjectListWidget( wxWindow* parent, std::shared_ptr<MyObjectListItemAccessor> controller, int panelheight); //! @brief パネル一枚の高さを取得 int GetPanelHeight()const { return _itemHeight; } //! @brief panelindex番のパネルが画面から見切れていたら、それが見えるように全体のパネルの配置をずらして調節する void FitPosition(int panelindex); //! @brief アクティブなアイテムを指定 void SetActiveItemIndex(int itemindex) { _activedItemIndex = itemindex; } //! @brief アクティブなアイテムを取得 int GetActiveItemIndex()const { return _activedItemIndex; } //! @brief アイテムの総数を取得 int Count()const; //! @brief 表示可能なアイテム数を計算 int GetDisplayableItemCount()const; //! @brief パネルの表示状態の更新 // 使用するパネルのみ表示し、表示するパネルのアイテム番号を指定し、そのパネルの表示内容を更新する void UpdateItemPanelList(); //! @brief サイズ変更イベント void OnSize(wxSizeEvent& event); //! @brief スクロール void OnScroll(wxScrollEvent& event); };
#include "MyObjectListWidget.hpp" // アイテムの総数を取得 int MyObjectListWidget::Count()const { return _itemaccessor->Size(); } // 表示可能なアイテム数を計算 int MyObjectListWidget::GetDisplayableItemCount()const { return _viewArea->GetSize().y / GetPanelHeight() + 1; } // パネルを表示 void MyObjectListWidget::SetItemPanels() { const int displayable = GetDisplayableItemCount(); const int created = _createdPanels.size(); const int panelHeight = GetPanelHeight(); if (created == displayable) { ; } else if (created < displayable) { for (size_t i = 0; i < created; i++) { _createdPanels[i]->SetSize(0, i * panelHeight, _viewArea->GetSize().x, panelHeight); if (!_createdPanels[i]->IsShown()) { _createdPanels[i]->Show(); } } // 表示可能なパネル数>定義済みパネル数の場合は // 足りていない分のパネルを追加 for (size_t i = created; i < displayable; i++) { ItemPanel* itemPanel = _itemaccessor->CreatePanel(_viewArea); itemPanel->SetSize(0, i * panelHeight, _viewArea->GetSize().x, panelHeight); _createdPanels.push_back(itemPanel); } } else { // 表示可能なパネル数<定義済みパネル数の場合は // 余ったパネルを非表示 for (size_t i = displayable; i < created; i++) { _createdPanels[i]->Hide(); } } // スクロールバーの現在位置 const int scrollpos = _scrollBar->GetThumbPosition(); // 表示しているパネル全てについての処理 // サイズのみ更新 for (size_t i = 0; i < displayable; i++) { _createdPanels[i]->SetSize(0, i * panelHeight, _viewArea->GetSize().x, panelHeight); if (!_createdPanels[i]->IsShown()) { _createdPanels[i]->Show(); } _createdPanels[i]->SetPanelInfo(this,i, scrollpos + i); _createdPanels[i]->UpdatePanel(); } } void MyObjectListWidget::FitPosition(int panelindex) { const int panelHeight = GetPanelHeight(); int fixpixel = 0; // アイテムの位置が上方向にはみ出している場合 if(_createdPanels[panelindex]->GetPosition().y < 0){ // 修正するピクセル数を計算 fixpixel = _createdPanels[panelindex]->GetPosition().y; } // アイテムの位置が下方向にはみ出している場合 else if (_createdPanels[panelindex]->GetPosition().y + panelHeight > _viewArea->GetSize().y) { // 修正するピクセル数を計算 fixpixel = _createdPanels[panelindex]->GetPosition().y + panelHeight - _viewArea->GetSize().y; } // 全てのパネルの位置を修正 for (size_t i = 0; i < _createdPanels.size(); i++) { _createdPanels[i]->SetPosition( wxPoint( _createdPanels[i]->GetPosition().x, _createdPanels[i]->GetPosition().y - fixpixel)); } // 必要な部分だけを再描画 _viewArea->Refresh(); _viewArea->Update(); } void MyObjectListWidget::UpdateItemPanelList() { SetItemPanels(); _viewArea->Layout(); } void MyObjectListWidget::PostCreate() { UpdateItemPanelList(); _scrollBar->SetScrollbar( 0, GetDisplayableItemCount(), _itemaccessor->Size(), 5, true); } MyObjectListWidget::MyObjectListWidget( wxWindow* parent, std::shared_ptr<MyObjectListItemAccessor> controller, int panelheight) : wxPanel(parent, wxID_ANY), _itemaccessor(controller), _itemHeight(panelheight) { // レイアウトマネージャーを作成 wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); // ViewAreaパネルを作成して追加 _viewArea = new wxPanel(this, wxID_ANY); sizer->Add(_viewArea, 1, wxEXPAND); //_viewAreaの背景を黒 _viewArea->SetBackgroundColour(wxColour(0, 0, 0)); // スクロールバーを作成して追加 _scrollBar = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL); sizer->Add(_scrollBar, 0, wxEXPAND); // レイアウトを設定 SetSizer(sizer); // サイズ変更時に表示内容の確認と更新 Bind(wxEVT_SIZE, &MyObjectListWidget::OnSize, this); // スクロールイベント Bind(wxEVT_SCROLL_CHANGED, &MyObjectListWidget::OnScroll, this); Bind(wxEVT_SCROLL_LINEUP, &MyObjectListWidget::OnScroll, this); Bind(wxEVT_SCROLL_LINEDOWN, &MyObjectListWidget::OnScroll, this); Bind(wxEVT_SCROLL_THUMBTRACK, &MyObjectListWidget::OnScroll, this); CallAfter(&MyObjectListWidget::PostCreate); Layout(); } // サイズ変更イベント void MyObjectListWidget::OnSize(wxSizeEvent& event) { // ウィンドウの高さが変わった場合、表示可能データ件数が変わり、 // スクロールバーの最大値が変わるため、再設定 // このとき、現在の表示位置を変えないように注意する _scrollBar->SetScrollbar( _scrollBar->GetThumbPosition(), GetDisplayableItemCount(), _itemaccessor->Size(), 5, true); UpdateItemPanelList(); event.Skip(); } // スクロール void MyObjectListWidget::OnScroll(wxScrollEvent& event) { UpdateItemPanelList(); event.Skip(); }
#pragma once #include <wx/wx.h> // 自作リストコントロールで一つのアイテムを表示するためのパネル class ItemPanel :public wxPanel { private: // このパネルが表示上で上から何番目かを表す int panelIndex = 0; class MyObjectListWidget* m_basepanel; size_t _item_index = 0; // このアイテムが指し示すアイテムのインデックス public: ItemPanel(wxWindow* parent); //! @brief 自作リストコントロールへのポインタを取得 class MyObjectListWidget* GetBasePanel() { return m_basepanel; } //! @brief このアイテムのインデックスを取得 size_t GetItemIndex()const { return _item_index; } //! @brief このパネルが表示上で上から何番目かを取得 int GetPanelIndex()const { return panelIndex; } //! @brief このアイテムパネルに各種情報をセット void SetPanelInfo(class MyObjectListWidget* base, int panelindex, size_t itemindex); //! @brief このパネルの表示内容を更新 void UpdatePanel(); /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// virtual size_t Size()const = 0; //! @brief itemindex番のアイテムが表示可能か、つまりインデクスの範囲内かをチェック //! @param itemindex アイテムのインデックス //! @return true: 表示可能 false: 表示不可 virtual bool IsValidItem(const size_t itemindex) = 0; //! @brief パネル一枚の表示を更新 //! @param panelIndex パネルのインデックス //! @param itemindex アイテムのインデックス //! @details スクロールが発生するたびにパネル内のウィジェットの表示内容を //! itemindex番の内容に更新する virtual void UpdatePanelItem(int panelIndex, size_t itemindex) = 0; //! @brief パネル一枚の表示を更新(インデクス範囲外の場合) //! @param panelIndex パネルのインデックス //! @details スクロールが発生して、パネルの表示内容を更新した際に、 //! itemindex番の内容が存在しない場合に呼び出される virtual void UpdatePanelOutOfRange(int panelIndex) = 0; //! @brief パネル一枚の再描画の内容を記述する //! @param dc 描画コンテキスト //! @param panelIndex パネルのインデックス //! @param itemindex アイテムのインデックス //! @details パネルのOnPaintイベント発生時に呼び出される。 //! 背景色などを変える場合はここに記載 virtual void PaintValid(wxDC& dc, int panelIndex, size_t itemindex) = 0; //! @brief パネル一枚の再描画の内容を記述する(インデクス範囲外の場合) //! @param dc 描画コンテキスト //! @param panelIndex パネルのインデックス //! @details パネルのOnPaintイベント発生時に呼び出される。 //! 背景色などを変える場合はここに記載 //! 特に表示内容がない場合は、背景色を変えるなどして、パネルが無いように見せる virtual void PaintOutOfRange(wxDC& dc, int panelIndex) = 0; void OnPaint(wxPaintEvent& event); //! @brief パネルをクリックしたときの挙動。 //! 有効なパネル(表示可能なアイテム)をクリックした場合は、OnEventLeftClickを呼び出す) //! 無効なパネル(表示不可能なアイテム)をクリックした場合は、OnEventLeftClickOutOfRangeを呼び出す virtual void OnLeftClick(wxMouseEvent& event); //! @brief 有効なパネルをクリックしたときの挙動 virtual void OnEventLeftClick(wxMouseEvent& event, int panelIndex, size_t itemindex); //! @brief 無効なパネルをクリックしたときの挙動 virtual void OnEventLeftClickOutOfRange(wxMouseEvent& event, int panelIndex); }; //! @brief パネルとパネルに表示するデータを関連付けるためのクラス //! @details パネルの生成時、各パネルはデータへアクセスできなければならない。 //! CreatePanel関数でパネルを作成するときに、データのポインタなどを用いて各パネルがデータへアクセスできるようにする. class MyObjectListItemAccessor { public: //! @brief パネルを生成する virtual ItemPanel* CreatePanel(wxWindow* parent) = 0; virtual ~MyObjectListItemAccessor() {}; //! @brief データの要素数を取得 virtual size_t Size()const = 0; };
#include "MyItemPanel.hpp" #include "MyObjectListWidget.hpp" ItemPanel::ItemPanel(wxWindow* parent) : m_basepanel(nullptr), wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxFULL_REPAINT_ON_RESIZE, wxPanelNameStr) { Bind(wxEVT_PAINT, &ItemPanel::OnPaint, this); Bind(wxEVT_LEFT_DOWN, &ItemPanel::OnLeftClick, this); } void ItemPanel::SetPanelInfo(class MyObjectListWidget* base, int panelindex, size_t itemindex) { panelIndex = panelindex; _item_index = itemindex; m_basepanel = base; } void ItemPanel::UpdatePanel() { if (IsValidItem(_item_index) == true) { UpdatePanelItem(panelIndex, _item_index); } else { UpdatePanelOutOfRange(panelIndex); } } void ItemPanel::OnPaint(wxPaintEvent& event) { ItemPanel* panel = (ItemPanel*)event.GetEventObject(); MyObjectListWidget* parent = panel->GetBasePanel(); wxPaintDC dc(panel); if (IsValidItem(panel->GetItemIndex()) == true) { PaintValid( dc, panel->GetPanelIndex(), panel->GetItemIndex() ); } else { PaintOutOfRange( dc, panel->GetPanelIndex() ); } } void ItemPanel::OnLeftClick(wxMouseEvent& event) { MyObjectListWidget* parent = this->GetBasePanel(); if (parent) { if (IsValidItem(this->GetItemIndex()) == false) { OnEventLeftClickOutOfRange( event, this->GetPanelIndex() ); } else { OnEventLeftClick( event, this->GetPanelIndex(), this->GetItemIndex() ); parent->FitPosition(this->GetPanelIndex()); } } } void ItemPanel::OnEventLeftClick(wxMouseEvent& event, int panelIndex, size_t itemindex) { MyObjectListWidget* parent = this->GetBasePanel(); parent->SetActiveItemIndex(itemindex); } void ItemPanel::OnEventLeftClickOutOfRange(wxMouseEvent& event, int panelIndex) { }
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #include"MyObjectListWidget.hpp" ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string> // データ struct MyData { std::string name; int index; };
//! @brief リストの各項目を表示するパネル class MyItemPanel :public ItemPanel { std::weak_ptr<std::vector<MyData>> m_pdata; wxStaticText* _label; public: MyItemPanel(wxWindow* parent) : ItemPanel(parent){ _label = new wxStaticText(this, wxID_ANY, "<unknown>"); _label->SetPosition(wxPoint(10, 10)); // ラベルの背景色とテキスト色を設定 _label->SetBackgroundColour(wxColour(255, 255, 255)); // 白色の背景 _label->SetForegroundColour(wxColour(0, 0, 0)); // 黒色のテキスト _label->Show(); } void setData(std::weak_ptr<std::vector<MyData>> data) { m_pdata = data; } virtual size_t Size()const override { return m_pdata.lock()->size(); }; virtual bool IsValidItem(const size_t itemindex)override { auto datalist = m_pdata.lock(); if (itemindex < datalist->size()) return true; return false; } virtual void UpdatePanelItem(int panelIndex, size_t itemindex)override { auto data = m_pdata.lock(); if (_label) { _label->SetLabel( wxString::Format( "%d Item %s [%d]", panelIndex, (*data)[itemindex].name.c_str(), (*data)[itemindex].index ) ); } // パネルの再描画 Refresh(); }; virtual void UpdatePanelOutOfRange(int panelIndex)override { auto data = m_pdata.lock(); if (_label) { _label->SetLabel("out of range"); } // パネルの再描画 Refresh(); } virtual void PaintValid(wxDC& dc, int panelIndex, size_t itemindex)override { dc.SetPen(wxPen(wxColour(0, 0, 0), 1)); if (itemindex == this->GetBasePanel()->GetActiveItemIndex()) { dc.SetBrush(wxBrush(wxColour(255, 0, 0))); } else { dc.SetBrush(wxBrush(wxColour(200, 200, 255))); } wxSize size = GetSize(); dc.DrawRectangle(0, 0, size.x, size.y); } virtual void PaintOutOfRange(wxDC& dc, int panelIndex)override { dc.SetPen(wxPen(wxColour(255, 0, 200), 2)); dc.SetBrush(wxBrush(wxColour(255, 0, 200))); wxSize size = GetSize(); dc.DrawRectangle(0, 0, size.x, size.y); } };
//! @brief リストの各項目を生成するクラス class MyOLAccessor :public MyObjectListItemAccessor { std::weak_ptr<std::vector<MyData>> m_data; public: MyOLAccessor(std::weak_ptr<std::vector<MyData>> data) : m_data(data) {} virtual ItemPanel* CreatePanel(wxWindow* parent)override { auto ptr = new MyItemPanel(parent); ptr->setData(m_data); return ptr; } virtual size_t Size()const override{ return m_data.lock()->size(); } };
// ウィンドウ作成 class MyFrame : public wxFrame { std::shared_ptr<std::vector<MyData>> m_data; public: void PostCreate() { m_data = std::make_shared<std::vector<MyData>>(); for(size_t i = 0; i < 13; i++) { m_data->push_back({ "name " + std::to_string(i), (int)i }); } MyObjectListWidget* objList = new MyObjectListWidget( this, std::make_shared<MyOLAccessor>(m_data), // データへアクセス 80 // アイテムの高さ ); objList->SetSize(0, 0, 200, 200); this->Layout(); // レイアウトの更新 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);