{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2023年度計算機演習A・B\n",
    "\n",
    "# 第13回：ベジェ曲線2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 曲線のパラメータ表示\n",
    "\n",
    "次の方程式で表される楕円を考えます。\n",
    "\n",
    "$$\n",
    "x^2+4y^2=1\n",
    "$$\n",
    "\n",
    "この楕円上の点は、次のように表すことができます。\n",
    "\n",
    "$$\n",
    "x(t)=\\cos(2\\pi t),\\quad y(t)=\\frac{1}{2}\\sin(2\\pi t)\\quad (0\\leq t\\leq 1)\n",
    "$$\n",
    "\n",
    "パラメータ $t$ の変化に従って、点 $(x(t),y(t))$ は一つの曲線（ここでは楕円）を描きます。\n",
    "\n",
    "このように各座標についてパラメータを用いた式で曲線を表すことを、曲線の**パラメータ表示（parametric representation、媒介変数表示）**と言います。\n",
    "\n",
    "パラメータ表示を利用すれば、陽関数として $y$ を $x$ の式で表すことの難しい曲線であっても、次のコードのように簡単に描画することができます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "t = np.linspace(0,1,101)  #パラメータt\n",
    "\n",
    "x = np.cos(2*np.pi*t)  #tを用いたx座標の式\n",
    "y = (1/2)*np.sin(2*np.pi*t)  #tを用いたy座標の式\n",
    "\n",
    "plt.plot(x,y)  #曲線の描画\n",
    "\n",
    "plt.grid()\n",
    "plt.gca().set_aspect(\"equal\")  #アスペクト比（縦横比）を1:1にする\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 演習1\n",
    "\n",
    "自然数 $a$ と $b$ に対して、パラメータ表示\n",
    "\n",
    "$$\n",
    "x(t)=\\sin(a\\cdot 2\\pi t),\\quad y(t)=\\sin(b\\cdot 2\\pi t)\\quad (0\\leq t\\leq 1)\n",
    "$$\n",
    "\n",
    "によって与えられる曲線を考えます。これは、**リサージュ曲線（Lissajous curve）**と呼ばれます。\n",
    "\n",
    "自然数 $a$ と $b$ に対するリサージュ曲線を描画する関数`draw_Lissajous(a,b)`を定義した上で、$a=4, b=5$ に対してその関数を呼び出してください。ただし、滑らかな曲線を描画すること。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#演習1のコード\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def draw_Lissajous(a,b):\n",
    "    #ここに、関数の処理を書く\n",
    "\n",
    "draw_Lissajous(4,5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. ベジェ曲線\n",
    "\n",
    "### 2.1. 定義\n",
    "\n",
    "$n$ 次**ベジェ曲線（Bezier curve）**は、前回扱ったバーンスタイン基底関数 $B_{i,n}(t)$ を用いてパラメータ表示\n",
    "\n",
    "$$\n",
    "x(t)=\\sum_{i=0}^n x_i B_{i,n}(t),\\quad y(t)=\\sum_{i=0}^n y_i B_{i,n}(t)\\quad (0\\leq t\\leq 1)\n",
    "$$\n",
    "\n",
    "によって与えられる曲線のことです。\n",
    "\n",
    "ここで、$x_0,\\ldots,x_n$ および $y_0,\\ldots,y_n$ は定数であり、$x$ 座標と $y$ 座標はこれらを係数とするバーンスタイン多項式として表されています。\n",
    "\n",
    "$(n+1)$ 個の点 $p_0=(x_0,y_0),\\ldots,p_n=(x_n,y_n)$ をベジェ曲線の**制御点（control points）**と呼び、名前の通り制御点の値を変化させることによってベジェ曲線の形状を制御することができます。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2. $3$ 次ベジェ曲線の性質\n",
    "\n",
    "$3$ 次ベジェ曲線を考えます。このとき、\n",
    "\n",
    "$$\n",
    "\\begin{aligned}\n",
    "x(t) & =\\sum_{i=0}^3 x_i B_{i,3}(t)=x_0\\cdot(1-t)^3+x_1\\cdot 3t(1-t)^2+x_2\\cdot 3t^2(1-t)+x_3\\cdot t^3,\\\\\n",
    "y(t) & =\\sum_{i=0}^3 y_i B_{i,3}(t)=y_0\\cdot(1-t)^3+y_1\\cdot 3t(1-t)^2+y_2\\cdot 3t^2(1-t)+y_3\\cdot t^3\n",
    "\\end{aligned}\n",
    "$$\n",
    "\n",
    "であり、制御点は $p_0=(x_0,y_0),\\ldots,p_3=(x_3,y_3)$ の $4$ 個です。\n",
    "\n",
    "$p(t)=(x(t),y(t))$ とすれば、\n",
    "\n",
    "$$\n",
    "p(0)=p_0,\\quad p(1)=p_3,\\quad p'(0)=3(p_1-p_0),\\quad p'(1)=3(p_3-p_2)\n",
    "$$\n",
    "\n",
    "が成り立ちます。ただし、$p'(t)=(x'(t),y'(t))$ は $t$ についての微分です。\n",
    "\n",
    "これらの式から、$3$ 次ベジェ曲線の性質として\n",
    "\n",
    "- $0$ 番目の制御点 $p_0$ と $3$ 番目の制御点 $p_3$ が両端になる（一般には、他の制御点 $p_1$ と $p_2$ は通らない）\n",
    "\n",
    "- $p_0$ における接線は $p_1$ に向かう方向であり、$p_3$ における接線は $p_2$ に向かう方向である\n",
    "\n",
    "ということが分かります。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上記の性質を確認するための例として、次の制御点によって定まる $3$ 次ベジェ曲線と、隣り合う制御点を結ぶ線分を描画してみます。\n",
    "\n",
    "$$\n",
    "p_0=(0,0),p_1=(1,2),p_2=(5,1),p_3=(3,0)\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import math\n",
    "\n",
    "def Bernstein(i,n,t):  #i番目のn次バーンスタイン基底関数のtにおける値を返す関数\n",
    "    return math.factorial(n)/(math.factorial(i)*math.factorial(n-i))*t**i*(1-t)**(n-i)\n",
    "\n",
    "P = np.array([[0,0],[1,2],[5,1],[3,0]]).T  #制御点の座標を並べた2×4の行列\n",
    "plt.plot(P[0,:],P[1,:],\"bo-\")  #制御点を結ぶ線分の描画\n",
    "\n",
    "t = np.linspace(0,1,101)  #パラメータt\n",
    "\n",
    "x = 0\n",
    "y = 0\n",
    "for i in range(4):  #3次ベジェ曲線のx座標とy座標の計算\n",
    "    x = x+P[0,i]*Bernstein(i,3,t)\n",
    "    y = y+P[1,i]*Bernstein(i,3,t)\n",
    "\n",
    "plt.plot(x,y,\"r\")  #ベジェ曲線の描画\n",
    "\n",
    "plt.grid()\n",
    "plt.gca().set_aspect(\"equal\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3. $3$ 次ベジェ曲線による図の描画\n",
    "\n",
    "$3$ 次ベジェ曲線は上で確認した性質を持つために扱いやすく、複数の $3$ 次ベジェ曲線を組み合わせることで様々な図を描画することができます。\n",
    "\n",
    "パソコンの描画ソフトでも、ベジェ曲線はよく使用されています。例えば、Windows標準の「ペイント」では、曲線を描画する際に始点 $p_0$ から終点 $p_3$ までドラッグし、その後にクリックする二つの点が順に $p_1$ と $p_2$ になるようにベジェ曲線の描画を行います。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 演習2\n",
    "\n",
    "制御点の座標を並べた行列 $P$ に対する $3$ 次ベジェ曲線を描画する関数`draw_Bezier(P)`を定義した上で、複数の $3$ 次ベジェ曲線を組み合わせて自由に図形や文字などを描画してください。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#演習2のコード\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import math\n",
    "\n",
    "def Bernstein(i,n,t):  #i番目のn次バーンスタイン基底関数のtにおける値を返す関数\n",
    "    return math.factorial(n)/(math.factorial(i)*math.factorial(n-i))*t**i*(1-t)**(n-i)\n",
    "\n",
    "def draw_Bezier(P):\n",
    "    #ここに、関数の処理を書く\n",
    "    #制御点を結ぶ線分の描画は不要\n",
    "    #plt.grid()、plt.gca().set_aspect(\"equal\")、plt.show()はコードの最後に書くため、ここには書かない\n",
    "\n",
    "#ここで、draw_Bezierを複数回呼び出す\n",
    "\n",
    "plt.grid()\n",
    "plt.gca().set_aspect(\"equal\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 第13回レポート課題\n",
    "\n",
    "演習1～演習2に取り組んでください。"
   ]
  }
 ],
 "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
}
