GFD Dennou Club / Dennou Ruby / Tutorials

Ruby-VTK Tutorial


VTKのインストール

まずはVTKをインストールします。

サンプルを動かして遊びましょう("Example" ディレクトリにあります)。

VTK については以下が役に立つと思います。

Ruby-VTKのインストール

次にRuby-VTKをインストールします。

現在 cygwin にはインストールできません。
VTK が "g++" の "-m win32" オプション付でコンパイルされるのに対し、 cygwin official の ruby がおそらく 上記オプションなしでコンパイルされて いることが原因と考えられます。

Ruby-VTK HP から "ruby-vtk-{version}.tar.gz" をダウンロードします。
展開後、 "ruby-vtk-{version}" ディレクトリに入ります。
以下を実行してください。

%ruby extconf.rb
%make
%su -
#make install

エラー無く終われば無事インストール完了です。
エラーがでた場合は、 あなたの環境(OS, C++コンパイラ, rubyバージョン)と エラーメッセージを作者に報告してください。

サンプルを試しましょう。 "sample" ディレクトリにいくつかサンプルがあります。

Ruby-VTKをつかう

Ruby-VTKの使い方

では実際に Ruby-VTK を使ってみましょう。 使用するためには

require "vtk"

とします。 ロード時間が気になる方は必要なコンポーネントだけロードすることもできます (vtk/Common, vtk/Filtering, vtk/Graphics, vtk/IO, vtk/Imaging, vtk/Rendering, vtk/Patented, vtk/Parallel, vtk/Hybrid)。

とりあえず VTK の バージョンでも調べてみましょう

%irb -r vtk
irb(main):001:0> Vtk::VTK_VERSION
=> "4.2.6"

あなたがインストールした VTK のバージョンが表示されたはずです。

クラスの名前

VTK のクラスの名前は "vtkObject" のように 小文字の vtk が先頭についています。 Ruby-VTK ではすべてのクラスが Vtkモジュールの下に定義されています。 それぞれのクラスは "Vtk::Object" のように VTK のクラス名の先頭の vtk を除いた名前となります。
いくつか例外があります。 VTK には、 "vtk3DS vtk3DSImporter, vtk3DWidget" といったクラスがあります。 Ruby のクラス名(定数) は一文字目に数字をとることができませんので "Vtk::3DS" といった名前にはできません。 したがってこれらは "Vtk::H3DS, Vtk::H3DSImporter, VTK::H3DWidget" というようになります。 ちなみに、"H" はこれらが所属する "Hybrid" の "H" です。

クラス作成とメモリ解放

VTK では、クラス作成に "New()" 関数を呼びます。 Ruby-Vtk では、他のクラスと同様に "new" クラスメソッドを呼びます。

ren = Vtk::Renderer.new

VTK では、使い終わったら "Delete()" メソッドを呼んであげる必要があります。 Ruby-Vtk では、GCが面倒を見てくれるのでそのような操作は必要ありません。

メソッド名

メソッド名は VTKのメソッドの名前そのままです。
例えば、VTK で

vtkPoints *points vtkPoints::New();
points->InsertNextPoint(1,2,3);

は、Ruby-VTK では

points = Vtk::Points.new
points.InsertNextPoint(1,2,3)

となります。

Ruby-VTK オリジナル機能

Ruby-VTK オリジナルの機能が "vtk/Misc" です。 現在 "NArray" から "vtkArray" への変換が用意されています。

nary = NArray.byte(100).indgen
vary = nary.to_va # => Vtk::CharArray

nary = NArray.sint(100).indgen
vary = nary.to_va # => Vtk::ShortArray

nary = NArray.int(100).indgen
vary = nary.to_va # => Vtk::IntArray or Vtk::LongArray

nary = NArray.sfloat(100).indgen
vary = nary.to_va # => Vtk::FloatArray

nary = NArray.float(100).indgen
vary = nary.to_va # => Vtk::DoubleArray

使用例

では実際にNetCDFファイルデータを使って、 等値面を描く例を見てみましょう。
使用する ソースコード および データファイル はこちらです。 water.rb,water.nc

ソースコードを順番に見ていきましょう。

require "numru/gphys"
require "vtk"
include Vtk
include NumRu

p "open"
fname = "water.nc"
vname = "Cloud_water_content"
  
gphys = GPhys::IO.open(fname,vname)
shape = gphys.shape
val = gphys.val

x = gphys.coord(0).val
y = gphys.coord(1).val
yfact = (x.max-x.min)/(y.max-y.min)
z = gphys.coord(2).val
zfact = (x.max-x.min)/(z.max-z.min)
x = x.to_va
y = y.to_va
z = z.to_va
f = val.reshape!(shape[0]*shape[1]*shape[2]).to_va

まずは "GPhys" を用いて "NetCDF" ファイルからデータを読み出し、 "NArray" にし、 その後 "Vtk::Array" に変換しています。

data = RectilinearGrid.new
data.SetDimensions(*shape)
data.SetXCoordinates(x)
data.SetYCoordinates(y)
data.SetZCoordinates(z)
data.GetPointData.SetScalars(f)

ここから VTK の本番です。 "Vtk::RectilinearGrid" という格子点データ(不等間隔も許す)を作って、 先ほどのデータを入れています。

surface = ContourFilter.new
surface.SetInput(data)
surface.SetValue(0,0.0007)

0.0007の値の等値面を作ります。 データを変換する際はこのように "Filter" を利用します。

mapper = PolyDataMapper.new
mapper.SetInput(surface.GetOutput)
mapper.SetScalarModeToUsePointFieldData

actor = Actor.new
actor.SetMapper(mapper)
actor.GetProperty.SetOpacity(0.5)
actor.SetScale(1,yfact,zfact)

"Mapper", "Actor" は "Filter" を実際に表示するためのデータに変換します。 "SetOpacity" は透明度を設定しています。 "SetScale" は軸のスケールの設定です。

outline = RectilinearGridOutlineFilter.new
outline.SetInput(data)
mapper_ol = PolyDataMapper.new
mapper_ol.SetInput(outline.GetOutput)
actor_ol = Actor.new
actor_ol.SetMapper(mapper_ol)
actor_ol.SetScale(1,yfact,zfact)

アウトラインを作っています。

#axes
bounds = data.GetBounds
length = data.GetLength
axes = Axes.new
axes.SetOrigin(bounds[0],bounds[2],bounds[4])
axes.SetScaleFactor(length*0.8)
tube_ax = TubeFilter.new
tube_ax.SetInput(axes.GetOutput)
tube_ax.SetRadius(length/50)
tube_ax.SetNumberOfSides(6)
mapper_ax = PolyDataMapper.new
mapper_ax.SetInput(tube_ax.GetOutput)
actor_ax = Actor.new
actor_ax.SetMapper(mapper_ax)

軸を作っています。

# graphics stuff
renderer = Renderer.new
renWin = RenderWindow.new
renWin.AddRenderer(renderer)
iren = RenderWindowInteractor.new
iren.SetRenderWindow(renWin)

# read data  //set up renderer
renderer.AddActor(actor_ax)
renderer.AddActor(actor_ol)
renderer.AddActor(actor)
renderer.SetBackground(1,1,1)
renWin.SetSize(500,500)
renderer.SetBackground(0.1, 0.2, 0.4)

# interact with data
iren.Initialize

renWin.Render
iren.Start

実際に描画するウィンドウや、 マウスやキーボードの操作(おまかせ)を設定しています。

実際に実行してみましょう。 このデータは赤道域の雲水量のデータです。 なんだか雲らしいものが見えますね。

マウスの左ボタンでカメラの回転、 中ボタンでカメラの平行移動、 右ボタンでズームです。 キーボードの "q" で終了します。

スクリーンショット

使用例 その2 (GTKとの組み合わせ)

先ほどの例で異なる値の等値面を見たい場合は、 ソースコードを書き換える必要があります。 なにかマウスでボタンをスライドさせて インターラクティブにその値を自由に変えてみたい、 なんて欲がでてきます。

VTKは比較的簡単に他のGUIツールと組み合わせることができます。 GTKを使う場合、 いくつかインストールする必要があります。

では次の rubyコードを実行してみましょう。
surface.rb

%ruby surface.rb water.nc Cloud_water_content

下にスライドがありますね。 動かしてみましょう。 等値面が変わるのが分かります。

スクリーンショット

同様に X,Y,Z面で切った断面をインターラクティブに動かす例です。
plane.rb

%ruby plane.rb water.nc Cloud_water_content
スクリーンショット