Rubyでクリエィティブコーディング


# はじめに

この記事は[Ruby Advent Calendar 2017](https://qiita.com/advent-calendar/2017/ruby)15日目の記事です。

[<< DXRubyのススメ](https://qiita.com/S_H_/items/85111f9ef9d8d513d010) || [ブロック引数の使いどころになる時のパターン >>](http://www.hassansan.me/entry/2017/12/13/181233)


遅くなってすみません💦
GTKについても調べながら書いているので、
間違いなどあったらご指摘お願いします。

今回はRubyでGTKを使ったクリエィティブコーディングについて書きたいと思います。
GTKと言ってもOpenGLを使った描画など、
色々あるそうなので今回はCairo(Gtk::DrawingArea)を使ったものを紹介します。

まずruby-gnome2をインストールします。
インストール方法は以前[こちら](https://qiita.com/enkatsu/items/a5b15ee2aa8913b4f554)に書いたので参考にしてください。

# 使い方

まずシンプルに円を表示します。
`context.scale 0.5, 0.5`しているのは、
なぜか2倍で表示されてしまうからなんですけどなんでなんでしょう……

**追記**
@noanoa07 さんからのコメントで以下の報告をいただきました。
Retina Displayじゃない方は`scale`はいらないみたいです。

> 2倍に表示されてしまう件は、HiDPi（高解像度）ディスプレイ（MacのRetina Displayなど）にCairoが対応してないからのようです。
なので、通常ディスプレイのMacだと普通に、Retina DisplayのMacだと2倍に表示されているのだと思います。
参考）
http://blog.livedoor.jp/noanoa07/archives/2154505.html

@ktou さんからのコメントで動的に`scale`を変更するように編集しました。

@ktou さんからのコメントを頂いて、以下の点を変更しました。

- HiDPIに対応したのでgtk3(3.3.0)を使用
- `scale` 関連のコードを削除
- `Cairo::Context` を生成せずに、 `:draw` のブロックに渡される `Cairo::Context` を使用

```ruby
require 'gtk3'

width = 300
height = 300

window = Gtk::Window.new
window.set_size_request(width, height)

drawing_area = Gtk::DrawingArea.new
drawing_area.set_size_request(width, height)

drawing_area.signal_connect(:draw) do |_, context|
  # background color
  context.set_source_rgb(0.0, 0.0, 0.0)
  context.paint 1.0

  # fill color
  context.set_source_rgb(1.0, 1.0, 1.0)
  # arc(x, y, r, start, end)
  context.arc(width / 2, height / 2, 150, 0, 2 * Math::PI)
  context.fill
end

fixed = Gtk::Fixed.new
fixed.put(drawing_area, 0, 0)
window.add(fixed)

window.show_all
window.signal_connect(:destroy) { Gtk.main_quit }

Gtk.main

```

![スクリーンショット 2017-12-15 3.25.09.png](https://qiita-image-store.s3.amazonaws.com/0/80816/5b3438e4-dd7c-2e1d-7132-ff7c40c82f91.png "スクリーンショット 2017-12-15 3.25.09.png")


# フラクタル

せっかくなんでよくある円の入れ子になったフラクタルも書きます。

```ruby
require 'gtk3'

width = 300
height = 300

window = Gtk::Window.new
window.set_size_request(width, height)

drawing_area = Gtk::DrawingArea.new
drawing_area.set_size_request(width, height)

def draw_circle(context, x, y, r, c = [1.0, 1.0, 1.0, 1.0])
  context.set_source_rgba(c)
  context.arc(x, y, r, 0, 2 * Math::PI)
  context.stroke
end

def draw_fractal_circle(context, x, y, r, c = [1.0, 1.0, 1.0, 1.0], n)
  return nil if n < 0
  draw_circle context, x, y, r, c
  next_r = r / 2
  x1 = x - next_r
  draw_fractal_circle context, x1, y, next_r, c, n - 1
  x2 = x + next_r
  draw_fractal_circle context, x2, y, next_r, c, n - 1
end

drawing_area.signal_connect(:draw) do |_, context|
  context.set_source_rgb(0.0, 0.0, 0.0)
  context.paint 1.0

  x = width / 2
  y = height / 2
  r = 150
  red = 0.1
  green = 0.7
  blue = 1.0
  alpha = 0.5
  draw_fractal_circle context, x, y, r, [red, green, blue, alpha], 5
end

fixed = Gtk::Fixed.new
fixed.put(drawing_area, 0, 0)
window.add(fixed)

window.show_all
window.signal_connect(:destroy) { Gtk.main_quit }

Gtk.main

```

![スクリーンショット 2017-12-15 3.47.27.png](https://qiita-image-store.s3.amazonaws.com/0/80816/37f5a5d7-a9fc-2bd0-d065-f3f7c10a99b5.png "スクリーンショット 2017-12-15 3.47.27.png")


# パーリンノイズ

パーリンノイズを生成するGemがあったのでこれを使えば、
ノイズを使った表現もできます。
[perlin_noise](https://github.com/junegunn/perlin_noise)

```ruby
require 'gtk3'
require 'perlin_noise'

width = 300
height = 300

window = Gtk::Window.new
window.set_size_request(width, height)

drawing_area = Gtk::DrawingArea.new
drawing_area.set_size_request(width, height)

def draw_circle(context, x, y, r, c = [1.0, 1.0, 1.0, 1.0])
  context.set_source_rgba(c)
  context.arc(x, y, r, 0, 2 * Math::PI)
  context.fill
end

n2d = Perlin::Noise.new 2, interval: 5
contrast = Perlin::Curve.contrast(Perlin::Curve::CUBIC, 2)

drawing_area.signal_connect(:draw) do |_, context|
  context.set_source_rgb(0.0, 0.0, 0.0)
  context.paint 1.0
  0.step(width, width / 10) do |x|
    0.step(height, height / 10) do |y|
      n = n2d[x * 0.01, y * 0.01]
      n = contrast.call n
      r = (width / 10 / 2) * n
      red = 1.0
      green = 1.0
      blue = 1.0
      alpha = n
      draw_circle context, x, y, r, [red, green, blue, alpha]
    end
  end
end

fixed = Gtk::Fixed.new
fixed.put(drawing_area, 0, 0)
window.add(fixed)

window.show_all
window.signal_connect(:destroy) { Gtk.main_quit }

Gtk.main

```

![スクリーンショット 2017-12-15 4.54.19.png](https://qiita-image-store.s3.amazonaws.com/0/80816/f1b611aa-8d88-4cd5-aaab-9f9a65fc4268.png "スクリーンショット 2017-12-15 4.54.19.png")

# まとめ

こんな感じでRubyでもクリエィティブコーディングしてみました。
今回は静止画 + 円縛りになっちゃいました。
これはこれでスケッチ感覚で楽しいですが、
インタラクションのあるものとかもやってみたいです。