クラインの壺の描き方（解読編）

### はじめに

　Qiitaには、これまでは仰向けのＧアイコンのままで参加してきました。しかし、初投稿から１年を過ぎて Contribution も50を超えたので、そろそろ自分専用のアイコンを作ろうと思い立ちました。

　アイコンの図柄は、ユーザ名 tsubolabo の tsubo に掛けて**クラインの壺**にしました。Wikipediaによれば、位相幾何学で非常に深い意味を持つ曲面のようですが、今のところ、そのような高度なことまでは考えておりません。掛詞以外の採用理由は、単に形が面白いというだけの軽いものです。

　しかし、アイコンとして採用するのであれば、自分でもこの図を描ける程度にはなりたいと精進してみました。その過程をここに記録しておきます。

### お手本の解読

　この画像を作るプログラムの例は、「MATLAB　クラインの壺」でググるとすぐに出てきます（プログラム１）。これは Symbolic Math Toolbox を使ったものですが、手を加えれば、素の MATLAB だけでも全く同様の**図１**が描けます。しかも処理時間は大幅に短くなります（プログラム２）。図１を上から見下ろしたのが今のアイコンです。
 
![klein_1_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/8a80c75a-8e4f-74c3-6b6d-2b4c0be5fcaf.png)

　プログラムの骨子を数式で示すと、媒介変数 $u$ , $v$ の範囲を $0\leq u\leq 2\pi$ , $\hspace{0.1ex}0\leq v\leq 2\pi$ として次のようになります。

　　 $r=4-2\cos u$
　　$\begin{align}
  x=
  \begin{cases}
    -4(1+\sin u)\cos u - r\cos u\cos v & (u\leq\pi) \\\\
    -4(1+\sin u)\cos u + r\cos v &  (u\gt\pi)
  \end{cases}
\end{align}$
　　 $y = r\sin v$
　　$\begin{align}
  z=
  \begin{cases}
    -14\sin u - r\sin u\cos v & (u\leq\pi)\\\\
    -14\sin u & (u\gt\pi)
  \end{cases}
\end{align}
$

　しかし、この式を一見するだけでは、なぜこれでクラインの壺が描けるのか見当もつきません。これまでの人生では、meshgrid による$X$, $Y$平面上の一価関数 $f(X,Y)$ の曲面を作図するだけで用が足りていました。媒介変数（しかも２つも）を使ったこのような複雑な曲面を考える機会はありませんでした。今回の解読は、老化防止用にちょうど良い、やや難度の高い課題です。

#### 手順１：中心線の抽出

　まずは、問題を単純化するために、$u$ の関数として与えられている $r$ の値を強制的に0に設定して、どんな図形になるのかを調べてみます。$r$ という変数名から見て、これは壺の各部の半径を表している数値であろうと予想できます。したがって、これを0とすることで、壺の中心線を求めることができそうです。

　その結果、グラフは**図２**のようになりました。図１の３Ｄ図を右斜め横から見ているので、縦軸は $y$ 軸ではなく $z$ 軸になっています。この目の位置からは見えませんが、$y$ 軸は画面の裏側に向かって伸びています。図中の各点の数字は媒介変数 $u$ の値です。$u$ の単位は本来はラジアンですが、分かりやすくするために、度に換算した10°間隔の値で表示しています。

　この状態を式で示せば次のとおりです。中心線は、$y{=}0$ 平面上に描かれた、媒介変数 $u$ の値だけで決まる曲線です。

　　$r=0$
　　$x=-4(1+\sin u)\cos u$
　　$y=0$
　　$z=-14\sin u$

　図２を見ると、曲線の形だけでなく、離散化された媒介変数の〇印の密度にもなにやら意味がありそうです。特に壺の入り口周辺には〇印が密集しています。

#### 手順２：Ｕ字管の縦断面

　半径 $r$ が変化する曲面を、一気に、しかも三次元で考えるのは難しそうです。まずは、$u$ によらず $r=3$ 一定であるような曲面を想定し、これと、平面 $y\hspace{-0.2em}=\hspace{-0.2em}0$ との交線を考えてみます。

　$y = r\sin v$ の関係式から、$y=0$ を満足する媒介変数 $v$ の値は、$v=0$ および $v=\pi$ であることが分かります。これを図示すると**図３**になります。$v=0$ のときの 等 $v$ 線が青色の線、$\hspace{0.2ex}v=\pi$ のときのそれが赤色の線です。
 
![klein_3_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/191acb3b-b56a-43c2-5c83-eafd7e3f3b01.png)

　図３の緑色の線は、各 $u$ 点において、$v$ を $0$～$\hspace{0.1ex}2\pi$ の範囲で変化させたときの等 $u$ 線です。この線の状態を、式と照らし合わせながら解釈してみます。先に示した中心線を表す $x$ と $z$ の式は、元の式の第１項だけで表されていました。それぞれの $u$ 点におけるこれらの値を $x_c$ と $z_c$ とし、さらに $r'=r\cos v$ と置けば、元の式は次のように書き替えられます。

　　$u\leq\pi$ （壺の下半分）のとき、
　　　　$x=x_c - r'\cos u$
　　　　$z=z_c - r'\sin u$
　　$u\gt\pi$ （壺の上半分）のとき、
　　　　$x=x_c + r'$
　　　　$z=z_c$


　これより、 $u\gt\pi$ となる図の上半分の領域では、それぞれの $u$ 点における $z$ の値は、$r'$ によらず常に一定であるため、図では水平線になっていることが分かります。また、$v{=}0$ のとき $r'{=}r$、$v{=}\pi$ のとき $\hspace{0.2ex}r'{=}{-}r$ なので、青色の線は中心線の右側（$x$ の正方向）に、赤色の線は左側（$x$ の負方向）に $r$ だけ離れます。

　一方、$u\leq\pi$ の下半分の領域では、点 $(x_c,z_c)$ からの変位量は $\delta x{=}{-}r'\cos u$、$\delta z{=}{-}r'\sin u$ となります。$\delta z$ と $\hspace{0.1ex}\delta x$ が作る角度 $\theta$ は、青線の $v=0$ に対応するものは、$r'{=}r$ より

　　$\hspace{0.1ex}\theta$$\hspace{0.1ex}\\,=\\,$$\hspace{0.1ex}\arg(\delta x+i\\,\delta z)$$\hspace{0.1ex}\\,=\\,$$\arg\\{-r(\cos u+i\\,\sin u)\\}$$\hspace{0.1ex}\\,=\\,$$u{+}\pi$

また、赤線の $v=\pi$ に対応するものは、$\hspace{0.2ex}r'{=}{-}r$ より

　　$\hspace{0.1ex}\theta$$\hspace{0.1ex}\\,=\\,$$\arg\\{r(\cos u+i\\,\sin u)\\}$$\hspace{0.1ex}\\,=\\,$$u$

となります。なお、ここで使っている $\theta$ は、$x$ を実軸、$z$ を虚軸とみなした複素平面上の角度です。中心線上の各 $u$ 点から、これらの方向に $r$ だけ離れた点が、青線、赤線上の対応 $u$ 点となります。

　ここまでで、媒介変数 $u$ の役割についてはほぼ理解できました。全体的に $u$ は角度的に利用されてはいますが、角度として意味が持たされているのは中心線の下半分の領域だけであり、上半分ではやや曖昧です。なんとなく、苦し紛れの無理やり感が漂ってきます。　

#### 手順３：Ｕ字管の輪切り線

　次に媒介変数 $v$ の役割を追っていきます。まず、この目的に都合が良いように式を変形します。ここでも、中心線上の各 $u$ 点$(x_c, y_c, z_c)$ からの変位量だけをとって考えます。（ ただし、$y_c=0$ とみなします）

　　$u\leq\pi$ （壺の下半分）のとき、
　　　　$\delta x=-r\cos u\cos v$
　　　　$\hspace{0.1ex}\delta y=r\sin v$
　　　　$\delta z=-r\sin u\cos v$
　　$u\gt\pi$ （壺の上半分）のとき、
　　　　$\delta x=r\cos v$
　　　　$\hspace{0.1ex}\delta y=r\sin v$
　　　　$\hspace{0.1ex}\delta z=0$

　これより、壺の上半分の領域では、$z=z_c$ 平面上に、点$(x_c, y_c, z_c)$ を中心とした半径 $r$ の円が描かれることが分かります。また、下半分を示す式は、点 $\hspace{0.1ex}(x_c, z_c)$ を通る画面に垂直な軸を中心に、$z=z_c$ 平面を角度 $u$ だけ回転した面を定義し、この面上に半径 $r$ の円が描かれることを示しています。

　$u$ の場合とは異なり、媒介変数 $v$ は明確に角度を表しています。この $v$ の正の方向の定義には、時計回り、反時計回りという表現は適確ではありません。$v=0$ の青線上の点が画面の裏側に回り込む向きが正になります。

　図３を斜めから見ると**図４**のようになります。緑色の等 $u$ 線は、図１の壺の表面の網目のうち、壺を輪切りにするように取り巻く線に対応しています。ただし、線が密集しすぎると見にくくなるので、1/3程度に間引いてあります。

#### 手順４：Ｕ字管の縦切り線

　次に、壺を縦切りにする等 $v$ 線について観察します。$v{=}0, \pi$ の青と赤の線については既に確認済です。その他の線がどうなるかは、図１からも容易に想像はつきますが、念のために描いてみると**図５**になります。線が多すぎると見難いので、60°ステップに間引きました。太線の黒〇で囲んだ継目部分の線の色の並びは、壺の上方から見て、下側では時計回りに青→茶→黄→紫→緑→水→青、上側では反時計回りに同順となっています。
 
![klein_5_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/932484/ccd104cb-9c58-0e89-a0a7-7d9484eac1a7.png)

#### 手順５：正規の半径を考慮

　実際の壺の半径を図３に反映させると**図６**になります。青線と赤線が壺らしい形に変化しました。これ以上特に説明を加えるまでもなく、なんとか図１との対応がつくようになりました。

### まとめ

　以上で、お手本がどのような考え方で描かれているのかはほぼ把握できました。図１とは異なる形の壺でも、マイナーチェンジで済むようなものであれば簡単に描けそうです。

　しかし、図１が原理に忠実に描かれた純正の壺なのか？少々ひっかかることもあります。また、もっと変形された独自の芸術的な壺を描くためには、さらなる工夫も要りそうです。引き続き精進中です。続編にご期待ください。

### プログラム
#### プログラム１
Symbolic Math Toolbox 版

```matlab:
syms u v;
r = @(u) 4 - 2*cos(u);
x = piecewise(u <= pi, -4*cos(u)*(1+sin(u)) - r(u)*cos(u)*cos(v), ...
    u > pi, -4*cos(u)*(1+sin(u)) + r(u)*cos(v));
y = r(u)*sin(v);
z = piecewise(u <= pi, -14*sin(u) - r(u)*sin(u)*cos(v),...
    u > pi, -14*sin(u));
h = fsurf(x,y,z, [0 2*pi 0 2*pi]);
```
#### プログラム２
素の MATLAB 版
```matlab:kleins.m
clear
close all

% 媒介変数u,v
u = linspace(0,2*pi,37);
v = linspace(0,2*pi,37);
[U,V] = meshgrid(u,v);
% U,V に対応する3D座標値 X,Y,Z
X = U*0;              % 必要サイズの行列を確保
Z = U*0;              % 　　　　　〃
idu1 = find(U<=pi);   % u<=pi を満足する要素のインデックス
idu2 = find(U>pi);    % U>pi を満足する要素のインデックス
R = 4 - 2*cos(U);     % 壺の各部の半径
X(idu1) = -4*cos(U(idu1)).*(1+sin(U(idu1))) ...
                                - R(idu1).*cos(U(idu1)).*cos(V(idu1));
X(idu2) = -4*cos(U(idu2)).*(1+sin(U(idu2))) + R(idu2).*cos(V(idu2));
Y = R.*sin(V);
Z(idu1) = -14*sin(U(idu1)) - R(idu1).*sin(U(idu1)).*cos(V(idu1));
Z(idu2) = -14*sin(U(idu2));

figure(1)
surf(X,Y,Z);
axis equal
```