qiita.comここの Python 版を Ruby に移植しただけです。計算に Gem 'numo-narray' を使っています。画像化には Gem 'gdk_pixbuf2', 'rmagick' を使いました。できたものはこんな感じ。
Ruby コード。Ruby 2.6.0, Linux Mint 19.2 で動作確認。
gray_scott.rb
require 'numo/narray' require 'gdk_pixbuf2' require 'rmagick' include Numo SPACE_GRID_SIZE = 256 VISUALIZATION_STEP = 8 Dx = 0.01 Du = 2e-5 Dv = 1e-5 def laplacian(ary, base) #NArray オブジェクトのコピー inflow_from_top = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE) inflow_from_bottom = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE) inflow_from_left = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE) inflow_from_right = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE) outflow = base.reshape(SPACE_GRID_SIZE, SPACE_GRID_SIZE) #ラプラシアンの計算 (SPACE_GRID_SIZE - 1).times do |i| inflow_from_top[i + 1, true] = ary[i, true] inflow_from_bottom[i, true] = ary[i + 1, true] inflow_from_left[true, i + 1] = ary[true, i] inflow_from_right[true, i] = ary[true, i + 1] end outflow = ary * 4 (inflow_from_top + inflow_from_bottom + inflow_from_left + inflow_from_right - outflow) / (Dx * Dx) end #計算(f, k の値によって反応が変わります) def calc(u, v, f, k) u_base = SFloat.ones(SPACE_GRID_SIZE, SPACE_GRID_SIZE) v_base = SFloat.zeros(SPACE_GRID_SIZE, SPACE_GRID_SIZE) VISUALIZATION_STEP.times do partial_u = laplacian(u, u_base) * Du - u * v * v + (1.0 - u) * f partial_v = laplacian(v, v_base) * Dv + u * v * v - (f + k) * v u += partial_u v += partial_v end [u, v] end #初期設定 Dir.chdir("picture") SQUARE_SIZE = 20 u = SFloat.ones(SPACE_GRID_SIZE, SPACE_GRID_SIZE) v = SFloat.zeros(SPACE_GRID_SIZE, SPACE_GRID_SIZE) square_start = SPACE_GRID_SIZE / 2 - SQUARE_SIZE / 2 square_end = SPACE_GRID_SIZE / 2 + SQUARE_SIZE / 2 u[square_start..square_end, square_start..square_end] = 0.5 v[square_start..square_end, square_start..square_end] = 0.25 #画像の生成 400.times do |i| u, v = calc(u, v, 0.022, 0.051) for_visualize = UInt8.cast(u * 255) data = UInt8.zeros(SPACE_GRID_SIZE, SPACE_GRID_SIZE, 3) data[true, true, 0] = for_visualize data[true, true, 1] = for_visualize data[true, true, 2] = for_visualize pixbuf = GdkPixbuf::Pixbuf.new(data: data.to_string, width: SPACE_GRID_SIZE, height: SPACE_GRID_SIZE) pixbuf.save("%04d.png" % i) end #gif化 list = Magick::ImageList.new Dir.glob("*").sort.each do |fn| list.concat(Magick::ImageList.new(fn)) end list.delay = 3 list.write("gifanime.gif")
作業用にカレントディレクトリに 'picture' ディレクトリを作って下さい。このコードの場合だと、400ステップ分の画像を生成します。
なお、ラプラシアンの差分方程式化はここなどが参考になります。
※参考
Gray-Scott というのを作ってみた記録 – ✈
Numo::NArray概要 · ruby-numo/numo-narray Wiki · GitHub
f = 0.012, k = 0.05 の場合。
Gtk によるリアルタイム・アニメーション版はこちら。
https://gist.github.com/obelisk68/ecd97e79b87f69365a0fb0747ffcedca