スポンサーリンク

VTKのファイル形式VTMをVTKを使わずに出力

VTM、VTPはXML形式なので、VTKを使わなくても出力できる。自分で作ったデータをvtm,vtpで出力するとparaview等で可視化できる。

XMLなのでpugixmlを使用。

save_vtm_vtkfree.hpp

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>

コメントを残す

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

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


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