スポンサーリンク

Boost::Graphでグリッド状のグラフを作成

#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();
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: