{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2023年度計算機演習A・B\n",
    "\n",
    "# 第15回：フラクタル2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 再帰的定義によるフラクタルの描画\n",
    "\n",
    "### 1.1. 関数の再帰的定義（復習）\n",
    "\n",
    "計算機演習Aの第5回授業では、「関数の中で自分自身を呼び出して利用する」という**関数の再帰的定義**の方法について学習しました。\n",
    "\n",
    "例えば、次のコードでは\n",
    "\n",
    "$$\n",
    "\\begin{gathered}\n",
    "1!=1,\\\\\n",
    "n!=n\\times(n-1)!\\quad (n\\geq 2)\n",
    "\\end{gathered}\n",
    "$$\n",
    "\n",
    "を用いることで、自然数`n`の階乗を計算する関数`factorial(n)`を定義しています。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def factorial(n):\n",
    "    if n == 1:  #関数をwell-definedにするために重要な意味を持つ\n",
    "        return 1\n",
    "    else:\n",
    "        return n*factorial(n-1)  #n-1に対して自分自身を呼び出す\n",
    "\n",
    "print(factorial(5))  #定義した関数を呼び出して値を表示する"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "このような関数の再帰的定義は、以下に示すようにフラクタルの描画の際に有用です。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2. コッホ曲線\n",
    "\n",
    "前回の授業で扱った**コッホ曲線**は、線分を下の図のように変形する操作を繰り返すことによって得られます。\n",
    "\n",
    "<div align=\"center\"><img src=\"https://www.ces-alpha.org/course/file_serve/5666228794294272/figure14-1.png\" width=\"450\"></div>\n",
    "\n",
    "関数の再帰的定義によってコッホ曲線を描画するコードは、次のようになります。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def draw_Koch(two_nodes,n):  #2つの点からなる行列two_nodesと、残りの繰り返し回数nが引数\n",
    "    if n == 0:  #残りの繰り返し回数が0なら線分を描画する\n",
    "        plt.plot(two_nodes[0,:],two_nodes[1,:],\"b\")\n",
    "    else:  #それ以外なら線分を変形する\n",
    "        p0 = two_nodes[:,0]\n",
    "        p1 = two_nodes[:,1]\n",
    "        p2 = 2/3*p0+1/3*p1\n",
    "        p3 = 1/3*p0+2/3*p1\n",
    "        theta = 60/180*np.pi\n",
    "        A = np.array([[np.cos(theta),-np.sin(theta)],[np.sin(theta),np.cos(theta)]])\n",
    "        p4 = p2+A@(p3-p2)\n",
    "        draw_Koch(np.array([p0,p2]).T,n-1)  #新たな各線分に対して、残りの繰り返し回数を1減らして自身を呼び出す\n",
    "        draw_Koch(np.array([p2,p4]).T,n-1)\n",
    "        draw_Koch(np.array([p4,p3]).T,n-1)\n",
    "        draw_Koch(np.array([p3,p1]).T,n-1)\n",
    "\n",
    "draw_Koch(np.array([[0,0],[1,0]]).T,5)\n",
    "plt.gca().set_aspect(\"equal\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3. ツリー\n",
    "\n",
    "線分を下の図のように変形する操作を繰り返すことによって得られるフラクタルを考え、これを**ツリー（tree）**と呼ぶことにします。\n",
    "\n",
    "※これは一般に知られているフラクタルではありませんが、木を表現するフラクタルはWeb検索でいくつも見つかるため、興味があれば調べてみてください。\n",
    "\n",
    "<div align=\"center\"><img src=\"https://www.ces-alpha.org/course/file_serve/5673975103356928/figure15-1.png\" width=\"250\"></div>\n",
    "\n",
    "関数の再帰的定義によってツリーを描画するコードは、次のようになります。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def draw_tree(two_nodes,n):  #2つの点からなる行列two_nodesと、残りの繰り返し回数nが引数\n",
    "    if n == 0:  #残りの繰り返し回数が0なら線分を描画する\n",
    "        plt.plot(two_nodes[0,:],two_nodes[1,:],\"b\")\n",
    "    else:  #それ以外なら線分を変形する\n",
    "        p0 = two_nodes[:,0]\n",
    "        p1 = two_nodes[:,1]\n",
    "        p2 = 2/3*p0+1/3*p1\n",
    "        p3 = 1/3*p0+2/3*p1\n",
    "        theta = 60/180*np.pi\n",
    "        A = np.array([[np.cos(theta),-np.sin(theta)],[np.sin(theta),np.cos(theta)]])  #反時計回りの回転\n",
    "        B = np.array([[np.cos(-theta),-np.sin(-theta)],[np.sin(-theta),np.cos(-theta)]])  #時計回りの回転\n",
    "        p4 = p2+A@(p3-p2)\n",
    "        p5 = p3+B@(p1-p3)\n",
    "        draw_tree(np.array([p0,p2]).T,n-1)  #新たな各線分に対して、残りの繰り返し回数を1減らして自身を呼び出す\n",
    "        draw_tree(np.array([p2,p3]).T,n-1)\n",
    "        draw_tree(np.array([p3,p1]).T,n-1)\n",
    "        draw_tree(np.array([p2,p4]).T,n-1)\n",
    "        draw_tree(np.array([p3,p5]).T,n-1)\n",
    "\n",
    "draw_tree(np.array([[0,0],[0,1]]).T,3)\n",
    "plt.gca().set_aspect(\"equal\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 第15回レポート課題\n",
    "\n",
    "フラクタルやベジェ曲線などを用いて、家を含む風景の図を描画してください。\n",
    "\n",
    "- 家は第11回レポート課題で作成したものを使用するとよいです。もちろん、さらに手を加えても構いません。\n",
    "\n",
    "- フラクタルを必ず使用してください。授業で扱ったフラクタルそのままではなく、自分で調べたもの・考えたもの・改造したものが望ましいです。\n",
    "\n",
    "- 授業ホームページの「昨年度の学生の作品」を参考にするとよいです。\n",
    "\n",
    "- 図の大きさを変更したい場合は、`plt.figure(figsize=(横幅,高さ))`を`plot`より前に書いてください。デフォルトの値は $(6.4,4.8)$ です。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#第15回レポート課題のコード"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### （作品の公開について）\n",
    "\n",
    "今回のレポート課題の提出フォームには、「来年度の授業ホームページでの作品の公開を承諾するか否か」の質問項目があります。\n",
    "\n",
    "皆さんが昨年度の学生の作品を参考にしているように、皆さんの作品の中から優秀なものを来年度の学生が参考にできるようにしたいと考えているためです。作品の公開は、基本的に作品の画像と氏名を掲載する形になります。\n",
    "\n",
    "三つの選択肢「承諾する」「匿名なら承諾する」「承諾しない」の中から、いずれかを自由に選んでください。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
