カメラ状態を保存リストアする。行列ではないので各ベクトルや値を個別に取り出さないといけない。
#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);