[Ruby]フラクタル図形を作るツールを作ってみた/ドラゴン曲線，コッホ曲線

# はじめに
前回フラクタルについて書いたので，今回**フラクタル図形**に関する内容を書きます．
2020/5/6 @scivola 様のアドバイスより，改修

# フラクタル図形
[フラクタル幾何 wikipedia](https://ja.wikipedia.org/wiki/%E3%83%95%E3%83%A9%E3%82%AF%E3%82%BF%E3%83%AB%E5%B9%BE%E4%BD%95)
フラクタル図形はフラクタル幾何とも言われるみたいですね．

ひとつの図形の中を拡大してみても，その小さな同じ図形が繰り返されているような図形のことです．

## フラクタル図形のプログラミング
このようにフラクタル図形は同じ図形が繰り返されている・・・
ということは**再帰**ですね．
そのためプログラミングにおいてフラクタル図形の描画は，再帰を学ぶ良い題材として扱われるみたいです．

今回は，フラクタル図形の中から**コッホ曲線**と**ドラゴン曲線**を実装してみようと思います．

# ツールの仕様
作成したツールの仕様は以下の通りです．
コマンドラインからの引数を受け取りフラクタル図形を`fractal_shape.png`として出力します．

```ruby

usage = <<"EOS"
fractal_shape.rb : フラクタル図形描画ツール
Usage:
  ruby fractal_shape.rb arg1 arg2 arg3

Description:
  ・コマンドライン引数として3つ入力してください
  ・fractal_shape.pngとして出力されます

Example:
  # 再帰回数3のコッホ曲線を1000×1000サイズで出力
  ruby fractal_shape.rb koch 1000 3   

  # 再帰回数10のドラゴン曲線を500×500サイズで出力
  ruby fractal_shape.rb dragon 500 10

Options:
  arg1：フラクタル図形の指定（コッホ曲線：'koch', ドラゴン曲線：'dragon'）
  arg2：出力するpng画像のサイズ
  arg3：フラクタルのステップ数

EOS
```


# Rubyによる実装

アルゴリズムの部分に関しては，以下の記事を参考にさせていただき，実装しました．
非常にわかりやすくまとまっており，アルゴリズムの部分は言語関係なく活用できますので是非．
[再帰プログラムによるフラクタル図形の描画](https://codezine.jp/article/detail/73)

入力はこんな感じで受け取ります．
引数がちゃんと3つ無い場合は`Usage`を表示します．

- 引数1：コッホ曲線かドラゴン曲線か
- 引数2：大きさ
- 引数3：繰り返し回数 

という具合で，クラスに渡します．

```ruby
if __FILE__ == $0
  if ARGV.size != 3
    puts usage
    exit
  end
  id = ARGV[0]
  width = height = ARGV[1].to_i
  step = ARGV[2].to_i

  fractal_shape = FractalShape.new(width, height, step)

  if id == "koch"
    fractal_shape.koch
  elsif id == "dragon"
    fractal_shape.dragon
  end
end
```

まずは`initialize()`で初期化を行います．
rubyで二次元描画どうするのかなあとかなり苦戦してしまいましたが
[cairo](https://rcairo.github.io/doc/ja/cairo-context.html)というgemを使ってみることにしました．


```ruby

  def initialize(width, height, step)
    @width = width
    @height = height
    @step = step
    @surface = Cairo::ImageSurface.new(width, height)
    @context = Cairo::Context.new(@surface)
    @context.set_source_rgb(1, 0, 0)
    @context.set_line_cap :round
  end
```

描画を行う関数です．
`context.move_to`から`context.line_to`にかけて形を作り
`context.stroke`で線分を描画します．
ここまではコッホ曲線，ドラゴン曲線ともに共通です．

```ruby

  def draw_line(s, e, context)
    context.move_to(s[0], s[1])
    context.line_to(e[0], e[1])
    context.stroke
  end
```

## コッホ曲線

まずはコッホ曲線の実装からです．
最初の部分は，コッホ曲線を描画する際に大きさ，向きをいい感じにするために適当な計算をしてます．

```ruby 

  def koch
    p = Vector[@width * 0.1, @height * 0.5]
    q = Vector[@width * 0.9, @height * 0.5]
    koch_algorithm(p, q, @step, @context)
    @surface.write_to_png("fractal_shape.png")
  end
```



### コッホ曲線のアルゴリズム
コッホ曲線のアルゴリズムです．
先ほどの記事が分かり易すぎるので，私が加筆する必要がありません・・・

```ruby

  def koch_algorithm(a, b, n, context)
    c = (a * 2 + b) / 3.0
    d = (a + b * 2) / 3.0

    xx = (b - a)[0]
    yy = -(b - a)[1]

    distance = Math.hypot(xx, yy) / Math.sqrt(3)

    if xx >= 0
      angle1 = Math.atan(yy.to_f / xx) + Math::PI / 6
      e = a + Vector[Math.cos(angle1), -Math.sin(angle1)] * distance
    else
      angle2 = Math.atan(yy.to_f / xx) - Math::PI / 6
      e = b + Vector[Math.cos(angle2), -Math.sin(angle2)] * distance
    end

    if n <= 0
      draw_line(a, c, context)
      draw_line(c, e, context)
      draw_line(e, d, context)
      draw_line(d, b, context)
    else
      koch_algorithm(a, c, n - 1, context)
      koch_algorithm(c, e, n - 1, context)
      koch_algorithm(e, d, n - 1, context)
      koch_algorithm(d, b, n - 1, context)
    end
  end
```

###結果

こんな感じで相似図形が出来上がっていきます．
![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/a6816473-4713-2f87-8ca6-09c9fa2bff15.png)![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/43328252-e531-db4b-9fbd-9d94a3eaae77.png)![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/e4588297-067b-575f-68dc-91c7b68ba15d.png)![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/bcd625c8-780d-397a-7e6b-020dfa49dd3c.png)





## ドラゴン曲線

次は本命**ドラゴン曲線**の実装です．
こちらも同じくドラゴン曲線を描画する際に大きさをいい感じにするために適当な計算をしてます．

```ruby 

  def dragon
    p = Vector[@width * 0.3, @height * 0.5]
    q = Vector[@width * 0.9, @height * 0.5]
    dragon_algorithm(p, q, @step, @context)
    @surface.write_to_png("fractal_shape.png")
  end
```



### ドラゴン曲線のアルゴリズム
ドラゴン曲線のアルゴリズムです．

```ruby

  def dragon_algorithm(a, b, n, context)
    xx = (b - a)[0]
    yy = -(b - a)[1]

    c = Vector[a[0]+(xx + yy)/2, b[1]+(xx + yy)/2]

    if n <= 0
      draw_line(a, c, context)
      draw_line(b, c, context)
    else
      dragon_algorithm(a, c, n - 1, context)
      dragon_algorithm(b, c, n - 1, context)
    end
  end
```

###結果

こんな感じで相似図形が出来上がっていきます．
![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/15f8052a-707f-7f72-f6e4-5aa84f38c3c5.png)![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/272e3a12-0bd6-9476-6cfe-bd16c08dde88.png)![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/0a691265-5a26-cce9-dc53-1b2762f4e625.png)![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/8ff516ac-a69c-58b6-93ec-8747debf0f00.png)


そしてステップ数が10回を超えてくると・・・

![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/80c62ae4-edc9-15d4-0702-4231c2f635d5.png)

**「ヘイウェイ・ドラゴン」を召喚！！**
> ヘイウェイ・ドラゴン（ハーター・ヘイウェイ・ドラゴンあるいはジュラシック・パーク・ドラゴンとも呼ばれる）は、NASAの物理学者のジョン・ヘイウェイ、ブルース・バンクスおよびウィリアム・ハーターによって初めて研究され、1967年、雑誌『サイエンティフィック・アメリカン（Scientific American）』のマーティン・ガードナーによるコラム「数学ゲーム（Mathematical Games」で紹介された。その性質についてはチャンドラー・デイビスとドナルド・クヌースによって初めて出版化された。マイケル・クライトンの小説『ジュラシック・パーク』の節ごとのタイトルページで使用されている。

さらに「ヘイウェイ・ドラゴン」2体を融合！
![fractal_shape.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535598/bbe6296c-f437-2bf6-adc1-bca91bb56eee.png)


**出でよ！「ツイン・ドラゴン」！！**
>二つのヘイウェイ・ドラゴンを背中合わせに配置することにより、ツインドラゴン（デイビス-クヌース・ドラゴンとも呼ばれる）を作ることが出来る。

粉砕！玉砕！大喝采！

なんてこともドラゴン曲線のアルゴリズムを，位置をずらして2回実行すれば簡単にできちゃいます．

# まとめ
ということで，~~ツイン・ドラゴンを召喚できました~~フラクタル図形を描画できました！
アルゴリズムさえ変えれば他のフラクタル図形も簡単に描画できますので
是非コピペして使ってみてください．

# 参考文献
再帰プログラムによるフラクタル図形の描画, 石立 喬[著] ,2005 https://codezine.jp/article/detail/73