#pragma once #include <vtkeigen/eigen/Dense> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/depth_first_search.hpp> #include <vtkGraphToPolyData.h> #include <vtkAssembly.h> #include <vtkProperty.h> #include <vtkGlyph3DMapper.h> #include <vtkSphereSource.h> #include <vtkActor.h> #include <vtkNew.h> #include <vtkPolyDataMapper.h>
// ------------------------------------------------------------------ // ノード情報 // ------------------------------------------------------------------ struct Node { Eigen::Vector3d coord; // 3次元座標 };
// ------------------------------------------------------------------ // グラフの型定義(無向グラフ) // ------------------------------------------------------------------ using boostGraph = boost::adjacency_list< boost::vecS, // OutEdgeList boost::vecS, // VertexList boost::undirectedS, // 無向グラフ Node, // 頂点プロパティ boost::no_property // 辺プロパティ(必要なら追加可能) >;
// ------------------------------------------------------------------ // 2次元等間隔グリッドグラフを作成する関数 // ------------------------------------------------------------------ //! @brief Nx × Ny の 2次元グリッドグラフを作成 //! @param Nx X方向のノード数 //! @param Ny Y方向のノード数 //! @param spacing 隣接ノード間の距離(デフォルト 1.0) //! @return 完成したグラフ boostGraph CreateGridGraph(int Nx, int Ny, double spacing = 1.0) { if (Nx <= 0 || Ny <= 0) { throw std::invalid_argument("Nx and Ny must be positive"); } boostGraph mygraph; // 1. 頂点を作成しつつ座標を格納 std::vector<boostGraph::vertex_descriptor> vertices; vertices.reserve(static_cast<std::size_t>(Nx * Ny)); for (int ix = 0; ix < Nx; ++ix) { for (int iy = 0; iy < Ny; ++iy) { size_t v = boost::add_vertex(mygraph); mygraph[v].coord = Eigen::Vector3d( ix * spacing, iy * spacing, 0.0 // 2次元なので z = 0 ); vertices.push_back(v); } } // 2. インデックス → 頂点記述子の変換 auto indexToID = [&](int ix, int iy) -> boostGraph::vertex_descriptor { return vertices[static_cast<std::size_t>(iy + Ny * ix)]; }; // 3. 4方向(上下左右)の辺を張る for (int ix = 0; ix < Nx; ++ix) { for (int iy = 0; iy < Ny; ++iy) { boostGraph::vertex_descriptor v = indexToID(ix, iy); // 右 (+x) if (ix + 1 < Nx) { boost::add_edge(v, indexToID(ix + 1, iy), mygraph); } // 上 (+y) if (iy + 1 < Ny) { boost::add_edge(v, indexToID(ix, iy + 1), mygraph); } } } return mygraph; }
vtkSmartPointer<vtkAssembly> CreateVTKActor(const boostGraph& mygraph) { // ============================================================== // 1. vtkPoints と vtkCellArray を手動で作る // ============================================================== vtkNew<vtkPoints> points; vtkNew<vtkCellArray> lines; // 辺(ライン) vtkNew<vtkCellArray> verts; // 頂点(Glyph用) // 頂点インデックス → VTKのpointId のマップは不要(順番に追加するだけ) points->SetNumberOfPoints(boost::num_vertices(mygraph)); // まず全頂点の座標を登録 boost::graph_traits<boostGraph>::vertex_iterator vi, vi_end; vtkIdType pointId = 0; for (boost::tie(vi, vi_end) = boost::vertices(mygraph); vi != vi_end; ++vi, ++pointId) { const Eigen::Vector3d& pos = mygraph[*vi].coord; points->SetPoint(pointId, pos.x(), pos.y(), pos.z()); // Glyph用に頂点セルも作っておく verts->InsertNextCell(1, &pointId); } // 次に全辺をラインとして登録 boost::graph_traits<boostGraph>::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = boost::edges(mygraph); ei != ei_end; ++ei) { auto u = boost::source(*ei, mygraph); auto v = boost::target(*ei, mygraph); vtkIdType ids[2] = { static_cast<vtkIdType>(boost::get(boost::vertex_index, mygraph, u)), static_cast<vtkIdType>(boost::get(boost::vertex_index, mygraph, v)) }; lines->InsertNextCell(2, ids); } // ============================================================== // 2. PolyData 作成 // ============================================================== vtkNew<vtkPolyData> polyData; polyData->SetPoints(points); polyData->SetLines(lines); polyData->SetVerts(verts); // Glyph3DMapper がこれを使う // ============================================================== // 3. ノード(球)のActor // ============================================================== vtkNew<vtkSphereSource> sphere; sphere->SetRadius(0.12); sphere->SetPhiResolution(18); sphere->SetThetaResolution(18); vtkNew<vtkGlyph3DMapper> glyphMapper; glyphMapper->SetInputData(polyData); glyphMapper->SetSourceConnection(sphere->GetOutputPort()); glyphMapper->ScalingOff(); // 全部同じ大きさ glyphMapper->Update(); vtkNew<vtkActor> nodeActor; nodeActor->SetMapper(glyphMapper); nodeActor->GetProperty()->SetColor(0.1, 0.7, 1.0); // 青系 nodeActor->GetProperty()->SetSpecular(0.6); nodeActor->GetProperty()->SetSpecularPower(30); // ============================================================== // 4. 辺(ライン)のActor // ============================================================== vtkNew<vtkPolyDataMapper> lineMapper; lineMapper->SetInputData(polyData); vtkNew<vtkActor> lineActor; lineActor->SetMapper(lineMapper); lineActor->GetProperty()->SetColor(0.65, 0.65, 0.65); lineActor->GetProperty()->SetLineWidth(2.5); // ============================================================== // 5. まとめて返す(Assembly) // ============================================================== vtkNew<vtkAssembly> assembly; assembly->AddPart(nodeActor); assembly->AddPart(lineActor); return assembly.GetPointer(); }
