TikZで平面図形を描くのは比較的シンプルなコマンドで実現でき、日本語での解説も豊富にあります。本記事で使用するコマンドに関しては参考文献TikZ-1,TikZ-2,TikZ-3に載っています。これに対して立体図形は、tikz-3dplotなどの派生パッケージで描けるもののコマンドが複雑であり、日本語での解説も多くありません。ということで、平面図形用のコマンドだけで立体図形を描くことにしました。なお、以下では見栄えの都合上、座標を横ヴェクトルで表示します。
ここでは以下のような図の描画を目的とします。
描画例
ソースコードはこちら:
\newcommand\project[3]{{(2*(#1)+(#2))/sqrt(5)},{(-(#1)+2*(#2)+5*(#3))/sqrt(30)}}
\newcommand\polarproj[3]{\project{(#1)*cos(#2)*cos(#3)}{(#1)*sin(#2)*cos(#3)}{(#1)*sin(#3)}}
% 九九九の表
\begin{tikzpicture}[scale=0.6]
\foreach \x in {1, 2, ..., 9}{
\foreach \y in {9, 8, ..., 1}{
\foreach \z in {1, 2, ..., 9}{
\draw[fill=white, thick](\project\x\y\z)circle(0.5)node{\pgfmathparse{int(\x*\y*\z)}$\pgfmathresult$};
}
}
}
\end{tikzpicture}\qquad
% レムニスケートの半分の回転体
% r^2=-cos2φ, (x,y,z)=(rcosθcosφ,rsinθcosφ,rsinφ), 0<z
\begin{tikzpicture}[scale=8]
% 経線?
\newcommand\varphiborder{atan(2*sin(\longitude)-cos(\longitude))/3}
\foreach \longitude in {15, 30, 45}
\draw[domain=\varphiborder+60:\varphiborder+120, variable=\latitude, samples=100]plot(\polarproj{sqrt(-cos(2*\latitude))}\longitude\latitude);
\foreach \longitude in {60, 75, 90, ..., 180}
\draw[domain=\varphiborder+60:135, variable=\latitude, samples=100]plot(\polarproj{sqrt(-cos(2*\latitude))}\longitude\latitude);
% 緯線?
\newcommand\thetaborder{acos(-tan(3*\latitude)/sqrt(5))}
\foreach \latitude in {46, 49, ..., 79}
\draw[domain=360-\thetaborder-atan(2):360+\thetaborder-atan(2), variable=\longitude, samples=100]plot(\polarproj{sqrt(-cos(2*\latitude))}\longitude\latitude);
\foreach \latitude in {82, 85, 88}
\draw[domain=0:360, variable=\longitude, samples=100]plot(\polarproj{sqrt(-cos(2*\latitude))}\longitude\latitude);
% 輪郭線
\newcommand\latitude{(\varphiborder+60)}
\draw[thick, domain=0:360-acos(1/sqrt(5))-atan(2), variable=\longitude, samples=100]plot(\polarproj{sqrt(-cos(2*\latitude))}\longitude\latitude);
\end{tikzpicture}
この図は、立体をある方向から見たときの様子になっています。もう少し数学的な言葉で言えば、ある平面への射影です。
点
と表すことができ、
です。これで三次元空間上の点を平面に落とし込むことができました。次ににこれを
となるので、
この線型写像による像を描画します。自分で行列計算をして結果をソースコードに書き入れるとごちゃごちゃするので、
% (\project{x}{y}{z}) -> ({X},{Y})
\newcommand\project[3]{{(2*(#1)+(#2))/sqrt(5)},{(-(#1)+2*(#2)+5*(#3))/sqrt(30)}}
のようにコマンドを定義しておきます。例えば立方体の場合、
% 立方体の描画
\newcommand\project[3]{{(2*(#1)+(#2))/sqrt(5)},{(-(#1)+2*(#2)+5*(#3))/sqrt(30)}}
\begin{tikzpicture}
\draw(\project000)--(\project100)--(\project110)--(\project010)--cycle;
\draw(\project001)--(\project101)--(\project111)--(\project011)--cycle;
\draw(\project000)--(\project001);
\draw(\project100)--(\project101);
\draw(\project110)--(\project111);
\draw(\project010)--(\project011);
\end{tikzpicture}
とすればよいです。
立方体
余談ですが、何故かこの図だと奥行きが小さいように見える気がします。透視図法を使えば良いのかもしれませんが…。
とパラメータ表示できるので、
% 球面の描画
\newcommand\project[3]{{(2*(#1)+(#2))/sqrt(5)},{(-(#1)+2*(#2)+5*(#3))/sqrt(30)}}
\begin{tikzpicture}
% 輪郭線
\draw(0,0)circle(1);
% longitude=θ, latitude=φ
% 経線
\foreach \longitude in {15, 30, ..., 180}
\draw[very thin, domain=0:360, variable=\latitude, samples=100]plot(\project{cos(\longitude)*cos(\latitude)}{sin(\longitude)*cos(\latitude)}{sin(\latitude)});
% 緯線
\foreach \latitude in {-75, -60, ..., 75}
\draw[very thin, domain=0:360, variable=\longitude, samples=100]plot(\project{cos(\longitude)*cos(\latitude)}{sin(\longitude)*cos(\latitude)}{sin(\latitude)});
\end{tikzpicture}
とできます。なお、簡便化のため球面座標でプロットするコマンドも作っておきます。
% 球面の描画 (球面座標系)
\newcommand\project[3]{{(2*(#1)+(#2))/sqrt(5)},{(-(#1)+2*(#2)+5*(#3))/sqrt(30)}}
% (\polarproj{r}{θ}{φ}) -> (\project{rcosθcosφ}{rsinθcosφ}{rsinφ})
\newcommand\polarproj[3]{\project{(#1)*cos(#2)*cos(#3)}{(#1)*sin(#2)*cos(#3)}{(#1)*sin(#3)}}
\begin{tikzpicture}
% 輪郭線
\draw(0,0)circle(1);
% longitude=θ, latitude=φ
% 経線
\foreach \longitude in {15, 30, ..., 180}
\draw[very thin, domain=0:360, variable=\latitude, samples=100]plot(\polarproj1\longitude\latitude);
% 緯線
\foreach \latitude in {-75, -60, ..., 75}
\draw[very thin, domain=0:360, variable=\longitude, samples=100]plot(\polarproj1\longitude\latitude);
\end{tikzpicture}
単位球
でパラメータ表示される二次元トーラスを前節の方法でプロットすると、以下のようになります。
% トーラスの描画1
\newcommand\project[3]{{(2*(#1)+(#2))/sqrt(5)},{(-(#1)+2*(#2)+5*(#3))/sqrt(30)}}
\begin{tikzpicture}
% longitude=θ, latitude=φ
% 経線?
\foreach \longitude in {10, 20, ..., 360}
\draw[domain=0:360, variable=\latitude, samples=100]plot(\project{cos(\longitude)*(3+cos(\latitude))}{sin(\longitude)*(3+cos(\latitude))}{sin(\latitude)});
% 緯線?
\foreach \latitude in {30, 60, ..., 360}
\draw[domain=0:360, variable=\longitude, samples=100]plot(\project{cos(\longitude)*(3+cos(\latitude))}{sin(\longitude)*(3+cos(\latitude))}{sin(\latitude)});
\end{tikzpicture}
二次元トーラス (美しくない)
これは美しくありません。本来見えないはずの裏側の線が見えていることが主原因だと考え、見える部分だけを表示するとこうなります(ついでに輪郭線を書き加えておきました)。
二次元トーラス (美しい)
この図の描画方法を考えましょう。
とおくと、点
に平行です(証明は省きますが、この部分空間の次元は常に
となる場合なので、3本のヴェクトルから作られる行列式が
が解の1つとして求まります。これを基にして曲線の描画範囲を決定します。ここからは厳密に議論するとややこしくなりそうなので、図を見て考えます。まずはatan
はこの主値の取り方)、acos
はこの主値の取り方)
と定めるとします。このとき回転軸から見て外側の領域(
とすればよいことが分かります。ということで、先程の図は以下のようにして描けます。
% トーラスの描画2
\newcommand\project[3]{{(2*(#1)+(#2))/sqrt(5)},{(-(#1)+2*(#2)+5*(#3))/sqrt(30)}}
\begin{tikzpicture}
% longitude=θ, latitude=φ
% 経線?
\newcommand\varphione{atan(2*sin(\longitude)-cos(\longitude))}
\newcommand\varphitwo{\varphione+180}
\foreach \longitude in {10, 20, ..., 360}
\draw[domain=\varphione:\varphitwo, variable=\latitude, samples=100]plot(\project{cos(\longitude)*(3+cos(\latitude))}{sin(\longitude)*(3+cos(\latitude))}{sin(\latitude)});
% 緯線?
\newcommand\thetaone{-acos(-tan(\latitude)/sqrt(5))-atan(2)}
\newcommand\thetatwo{acos(-tan(\latitude)/sqrt(5))-atan(2)}
\foreach \latitude in {-60, -30, ..., 60}
\draw[domain=\thetaone:\thetatwo, variable=\longitude, samples=100]plot(\project{cos(\longitude)*(3+cos(\latitude))}{sin(\longitude)*(3+cos(\latitude))}{sin(\latitude)});
\foreach \latitude in {90}
\draw[domain=0:360, variable=\longitude, samples=100]plot(\project{cos(\longitude)*(3+cos(\latitude))}{sin(\longitude)*(3+cos(\latitude))}{sin(\latitude)});
\foreach \latitude in {120, 150, ..., 240}
\draw[domain=\thetatwo:\thetaone+360, variable=\longitude, samples=100]plot(\project{cos(\longitude)*(3+cos(\latitude))}{sin(\longitude)*(3+cos(\latitude))}{sin(\latitude)});
% 輪郭線
\foreach \latitude in {\varphione, \varphitwo}
\draw[thick, domain=0:360, variable=\longitude, samples=100]plot(\project{cos(\longitude)*(3+cos(\latitude))}{sin(\longitude)*(3+cos(\latitude))}{sin(\latitude)});
\end{tikzpicture}
理論的な話は前節までで終わりです。ここからは実際に、平面図形用のコマンドだけで色々な空間図形を描画してみます。
立方体を切断する図を描きます。上で描いた立方体は見えない辺も実線で描きましたが、今回は点線で描きます。
% 立方体の切断
\newcommand\project[3]{{(2*(#1)+(#2))/sqrt(5)},{(-(#1)+2*(#2)+5*(#3))/sqrt(30)}}
\begin{tikzpicture}
% 3つの立方体
\foreach \shift in {0, 4, 8}{
\draw[thick, shift={(\shift,0)}](\project002)--(\project022)--(\project222)--(\project220)--(\project200)--(\project000)--cycle--(\project202)--(\project222);
\draw[thick, shift={(\shift,0)}](\project200)--(\project202);
\draw[thick, dashed, shift={(\shift,0)}](\project020)--(\project220);
\draw[thick, dashed, shift={(\shift,0)}](\project000)--(\project020)--(\project022);
}
% 三角形の切断面
\fill[red, opacity=0.3](\project200)--(\project222)--(\project002)--cycle;
% 五角形の切断面
\fill[red, opacity=0.3, shift={(4,0)}](\project200)--(\project22{4/3})--(\project122)--(\project012)--(\project00{4/3})--cycle;
% 六角形の切断面
\fill[red, opacity=0.3, shift={(8,0)}](\project100)--(\project210)--(\project221)--(\project122)--(\project012)--(\project001)--cycle;
\end{tikzpicture}
立方体の切断
二変数の標準正規分布の確率密度関数のグラフを描きます。目標はこちら(参考:多変量正規分布)。
二変数標準正規分布
確率密度函数は
を描画します。輪郭線を計算しましょう。接平面を生成するヴェクトルは
ですから、
です。この方程式はexplicitに解けません。うまいこと変数変換すれば描画できそうな気がしないでもないですが、思いつかなかったので読者への演習問題とします。とりあえずの完成形はこちら。描画量を多くした結果PCが悲鳴を上げました。
% 二変数標準正規分布
\newcommand\project[3]{{(2*(#1)+(#2))/sqrt(5)},{(-(#1)+2*(#2)+5*(#3))/sqrt(30)}}
\begin{tikzpicture}
% 座標軸
\draw[-stealth](\project{-5}00)--(\project500)node[right]{$x$};
\draw[-stealth](\project0{-5}0)--(\project050)node[above right]{$y$};
\draw[-stealth](\project00{-2})--(\project005)node[above]{$z$};
% 曲面
\foreach \x in {-4, -3.9, ..., 4}
\draw[domain=-sqrt(16-\x*\x):sqrt(16-\x*\x), variable=\y, samples=100]plot(\project\x\y{4*exp(-(\x*\x+\y*\y)/2)});
\foreach \y in {-4, -3.9, ..., 4}
\draw[domain=-sqrt(16-\y*\y):sqrt(16-\y*\y), variable=\x, samples=100]plot(\project\x\y{4*exp(-(\x*\x+\y*\y)/2)});
\end{tikzpicture}
二変数標準正規分布
電磁波が伝わる様子を図示します。目標はこちら(参考:電磁波)。
電磁波
なんとなく電磁波は右上に向かって進んでほしいので、ここだけ目線の向きを変えます。具体的には、点
です。また、電場の波は
% 電磁波
% ここの射影コマンドは他と異なる
\newcommand\waveproject[3]{{(2*(#1)-(#2))/sqrt(5)},{((#1)+2*(#2)+5*(#3))/sqrt(30)}}
\begin{tikzpicture}
% 座標軸
\draw[-stealth](\waveproject{-1}00)--(\waveproject{6.5}00)node[right]{$x$};
\draw[-stealth](\waveproject0{-2}0)--(\waveproject020)node[above left]{$y$};
\draw[-stealth](\waveproject00{-2})--(\waveproject002)node[above]{$z$};
\draw[red, thick, domain=6:6.05]plot(\waveproject\x0{1.5*sin(180*\x)});
\draw[blue, thick, domain=6:6.05]plot(\waveproject\x{-1.5*sin(180*\x)}0);
\foreach \xshift in {5, 4, 3, ..., 0}{
% 電場
\draw[red, thick, domain=\xshift:\xshift+1, samples=100]plot(\waveproject\x0{1.5*sin(180*\x)});
\foreach \xxshift in {0.1, 0.2, ..., 0.9}
\draw[red, opacity=0.4, -stealth](\waveproject{\xshift+\xxshift}00)--(\waveproject{\xshift+\xxshift}0{1.5*sin(180*(\xshift+\xxshift))});
% 磁場
\draw[blue, thick, domain=\xshift:\xshift+1, samples=100]plot(\waveproject\x{-1.5*sin(180*\x)}0);
\foreach \xxshift in {0.1, 0.2, ..., 0.9}
\draw[blue, opacity=0.4, -stealth](\waveproject{\xshift+\xxshift}00)--(\waveproject{\xshift+\xxshift}{-1.5*sin(180*(\xshift+\xxshift))}0);
}
% 電磁場の波動方程式
\draw(\waveproject60{-3})node{$\begin{cases}
\varepsilon_0\mu_0\dfrac{\partial^2}{\partial t^2}\bm E=\Delta\bm E\\[.5zh]
\varepsilon_0\mu_0\dfrac{\partial^2}{\partial t^2}\bm B=\Delta\bm B
\end{cases}$};
\end{tikzpicture}
電磁波