フィボナッチ螺旋をPython3+matplotlibで描く

# はじめに
美しいフィボナッチ螺旋を自分で描いてみたかったのです。
螺旋は、原点Oを始点としてまず右下に伸び、反時計回りに描くようにしました。
# 環境
- Python 3.9.6
- matplotlib 3.4.2
- MacOS 11.5.2

# 手法

項ごとに、以下のように処理し、あたかも１本のグラフであるかのように見せています。

1. 90度の弧を描く
2. 各項の値を弧の半径とする
3. 弧を90度ずつ回転させる
4. 弧の中心をオフセットする

１本のグラフとして、ｘ・ｙの変域を細かく設定して描く方法もあるのでしょうが、今回はその方法は採っていません。

# コード
### モジュールのインポートとグラフ周りの設定

詳細は割愛。
グラフの範囲は縦横-25から25にしています。draw_areaで定義しています。

```python
# 使用するモジュールのインポート
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

# グラフの設定
fig = plt.figure(figsize=(15, 15), dpi=72, tight_layout=True)
fig.suptitle('Fibonacci', fontsize=30)
ax = fig.add_subplot(111)
draw_area = 25

# グラフの目盛り・補助線の設定
ax.set_xlim(-draw_area, draw_area)
ax.set_ylim(-draw_area, draw_area)
ax.set_aspect('equal')
ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax.grid(axis='both', color="blue", lw=0.5, ls="--")
```

### フィボナッチ数を生成する関数
もっとも（遅く）素朴なものを使ってます。

```python
def get_fib(n):
    if n == 0 or n == 1:
        return n
    else:
        return get_fib(n - 1) + get_fib(n - 2)
```

### 弧の中心を求める関数
手法4.で述べた弧の中心のオフセットを行います。
任意の基準点からの絶対位置を採るのではなく、前項からの相対位置を採るようにしています。
中心点を繋いだ軌跡は下図です。
![Origin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/383285/467c9be3-a348-cd69-afc4-a26d0a103c46.png)

0・1項目は固定値を与えるほかなかろうと。
2項目以降については、移動距離はふた項前の値でよいのですが、xyの両軸を交互に移動させ、かつ軸ごとに移動の向きを切り替える必要があるため、非常に泥臭い処理をしています。

```python
x, y = 0, 0 # 中心の初期値
xb, yb = -1, -1 # 移動の向きを正負で規定

def get_center_pos(n):
    global x, y, xb, yb
    if n == 0: # （0項目はそもそも軌跡がない）
        x, y = 0, 0
    elif n == 1: # 1項目は原点Oから右下への弧を描かせるのでx=1、y=0
        x, y = 1, 0
    elif n % 2 == 1:
        if xb == -1:
            x -= get_fib(n - 2)
            xb = +1
        else:
            x += get_fib(n - 2)
            xb = -1
    elif n % 2 == 0:
        if yb == -1:
            y += get_fib(n - 2)
            yb = +1
        else:
            y -= get_fib(n - 2)
            yb = -1
    return x, y
```

### 弧を描く処理
弧はmatplotlib.patchesのArcで描きます。angleは初期値は90です。
手法の1.〜3.について、以下のように実践しています。

1. 90度の弧を描く：theta2とtheta1の差分
2. 各項の値を弧の半径とする：widthとheightにget_fib(i) * 2を代入
3. 弧を90度ずつ回転させる：angleの値を90ずつ増分

```python
my_angle = 90
for i in range(0, 11): #最終値は任意
    my_arc = mpatches.Arc(xy=get_center_pos(i), width=get_fib(i) * 2, height=get_fib(i) * 2, angle=my_angle, 
                          theta1=0, theta2=90,linewidth=5, color="r")
    ax.add_patch(my_arc)
    my_angle += 90
plt.show()
```

# コード全体
上記の記述を繋げばよいです。
<details>
<summary>コード全体</summary>
<div>

```python
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

fig = plt.figure(figsize=(15, 15), dpi=72, tight_layout=True)
fig.suptitle('Fibonacci', fontsize=30)
ax = fig.add_subplot(111)
draw_area = 25

ax.set_xlim(-draw_area, draw_area)
ax.set_ylim(-draw_area, draw_area)
ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax.grid(axis='both', color="blue", lw=0.5, ls="--")
ax.set_aspect('equal')


def get_fib(n):
    if n == 0 or n == 1:
        return n
    else:
        return get_fib(n - 1) + get_fib(n - 2)


x, y = 0, 0
xb, yb = -1, -1


def get_center_pos(n):
    global x, y, xb, yb
    if n == 0:
        x, y = 0, 0
    elif n == 1:
        x, y = 1, 0
    elif n % 2 == 1:
        if xb == -1:
            x -= get_fib(n - 2)
            xb = +1
        else:
            x += get_fib(n - 2)
            xb = -1
    elif n % 2 == 0:
        if yb == -1:
            y += get_fib(n - 2)
            yb = +1
        else:
            y -= get_fib(n - 2)
            yb = -1
    return x, y


my_angle = 90
for i in range(0, 11):
    my_arc = mpatches.Arc(xy=get_center_pos(i), width=get_fib(i) * 2, height=get_fib(i) * 2, angle=my_angle, 
                          theta1=0, theta2=90,linewidth=5, color="r")
    ax.add_patch(my_arc)
    my_angle += 90
plt.show()
```

</div>
</details>


# 実行結果

下図のように、原点Oを始点としたフィボナッチ螺旋が描画されています。
![Fibonacci.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/383285/ab197772-bae1-d88e-7da8-bc8007668775.png)

# 最後に
次は、ｘ・ｙの変域を細かく設定して１本のグラフとして描く方法を試してみようかと。