MePoTeXによる図形グラフの作成

in [TeXの部屋]

MePoTeXを利用した図形やグラフの作成手順を取りまとめました。

MePoTeXを活用して,図形とグラフを究めよう!

  • MathJaxを使用しているので、 スマホでは表示に時間がかかることがあります。
  • モバイル利用(Android)でのメニュー選択は、 SiteMapを利用するか、 または「長押し」から「新しいタブを開く」を選択してください。
■MePoTeXによる図形やグラフの作成法 [Map]


[御案内] TeXを利用して数学プリントを作成するとき, いろいろな図形やグラフを作成する必要があります. それを作成するアプリはTeXのシステムにすでに含まれています. 「MetaPost」です.さらに,それをTeXソースの中で利用できるようにした パッケージが「MePoTeX」(概要)です.詳細なマニュアルもあるのですが, TeXを使い慣れていない場合は全体像を把握しにくいかもしれません.
 そこで,ここでは,TeXを使い始めた方を念頭に, MePoTeXを用いた図形やグラフの作成方法について解説します. ただし,TeXのインストールはすでになされており, TeXによる通常文書は作成できるレベルにあることを前提とします. emathと類似したコマンドで 曲面の描画まで行うことができ、 図形をGhostscriptを経由しないEPSファイル(MPS)で保存することができます。

  • 「emath」+「MePoTeX」があれば、 数学教材の作成には十分ではないかとさえ思えます。
  • 下記解説の詳細は、 MePoTeXやMetaPostのマニュアルを 参照してください。
  • MePoTeXの作者「みなも」氏には、 多大の敬意を表します。ver1.00は2000年に発表され、 最新版は2022年1月のver4.50です。 MePoTeXが広く利用されるようになることを祈念します。


■空間図形の基本

(注) Web上での表示の都合上、 空白は全角を使用しています。 以下のコードをコピーして実行するときは、 半角の空白かtabで置き換えて実行してください。


空間座標
空間図形を描くことは、立体を平面に「投影する」作業になります。 ここでは、その投影の仕組みには触れませんが、 MePoTeXでは点の平面への投影は「proj(座標)」により行われます。 したがって、線で結ばれた空間図形を描くには、 平面に投影された個々の点を「xdraw()」で結んでいくだけです。

投影の仕組みなどについては、下記の「第4部第7講 射影・投影」(p150)を 参照してください。

  • Metafont&MetaPostで楽々お絵かき [PDF]
下記では、底面が \(\small xy\) 平面上にある直方体を描画しています。 また、どのように描かれているかを示すために 座標軸も書き入れています。 軸が不要なときは該当部分をコメントアウトして、 辺が座標軸上にある箇所を xdaw() で個別に結んでください。

以下では、最初に、頂点の座標を個別に定義する場合を示します。 その後で、単位ベクトルを利用して 定義する場合を示しています。

  • \begin{MPpic}<1cm>(4,4) % [A]座標軸 \mptXTaxis|0w==4w>[r]   <1mm,0mm>{$x$} \mptYTaxis|0w==5w>[r]   <1mm,0mm>{$y$} \mptZTaxis|0w==3w>[b]   <1mm,0mm>{$z$} % [B]直方体の頂点 \mptPoint{z.O}[O][t]  <0mm,-1mm>{proj(0,0,0)} \mptPoint{z.A}[A][t]  <0mm,-1mm>{proj(3w,0,0)} \mptPoint{z.B}[B][tl]  {proj(3w,4w,0)} \mptPoint{z.C}[C][t]  <0mm,-1mm>{proj(0,4w,0)} \mptPoint{z.D}[D][bl]  <1mm,1mm>{proj(0,0,2w)} \mptPoint{z.E}[E][br]  {proj(3w,0,2w)} \mptPoint{z.F}[F][b]  <0mm,1mm>{proj(3w,4w,2w)} \mptPoint{z.G}[G][bl]  {proj(0,4w,2w)} % [C]直方体の描画 \sendMP{ xdraw() z.O--z.A--z.B   --z.C--cycle; xdraw() z.D--z.E--z.F   --z.G--cycle; xdraw() z.O--z.D; xdraw() z.A--z.E; xdraw() z.B--z.F; xdraw() z.C--z.G; xdraw() z.O--z.F; xdraw() z.O--z.B; } \end{MPpic}
上記では、最初に[A]で座標軸を定義しています。 空間の座標軸を引くコマンドは「\mptXTaxis」です。 書式は、

  \mptXTaxis|基点==終点>[配置]
    <横調整,縦調整>{ラベル}

とします。基点と終点は、\(\small x\) 軸を引く 座標を単位つきで指定します。 「==」の箇所をたとえば「=(2w,1w)=」とすると、 軸の基点が (0,2,1) の箇所になります。

次に、[B]で直方体の各頂点を個別に定義しています。 \mptPointを利用すると、点の座標の定義、その点に振るラベル(文字)、 そしてその配置位置までを一気に行うことができます。 xdraw()で結ぶには、「proj」をつけて投影した点で定義する必要があります。 頂点を定義したら、[C]で「xdraw( )」で結んでいくだけです。 ( )内では、線の太さ・線種・色などを指定することができます。


▲戻る(トップメニューマップ)

■単位ベクトルの利用
上記では頂点の座標を個別に定義しましたが、 単位ベクトルを利用する形で定義することもできます。 MetaPostでは、色指定の定数として black=(0,0,0)、red=(1,0,0)、green=(0,1,0)、blue=(0,0,1) が定義済みです。色指定の箇所で利用すると色ですが、 この値を空間の座標やベクトルの成分として利用することもできます。 平面のベクトルや座標を表す変数は「pair」で宣言する必要がありますが、 空間のベクトルや座標を表す変数は「color」で宣言します。 前述の直方体は、次のような形で定義することができます。
  • \begin{MPpic}<1cm>   (4|4,4|1) % [A]座標軸 \mptXTaxis|0w==4w>[r]   <1mm,0mm>{$x$} \mptYTaxis|0w==5w>[l]   <1mm,0mm>{$y$} \mptZTaxis|0w==3w>[b]   <0mm,1mm>{$z$} \sendMP{ % [B]変数宣言  color e[],c[]; % [C]基本ベクトルの定義  e0=black;e1=red;  e2=green;e3=blue; % [D]頂点の定義  c0=e0; c1=3*e1;  c3=4*e2; c2=c1+c3;  c4=2*e3; c5=c1+c4;  c7=c3+c4; c6=c2+c4; % [E]頂点を射影  for n=0 upto 7:   z[n]=w*proj(c[n]);  endfor; % [F]直方体の描画  for n=0,4:   xdraw() z[n]--z[n+1]    --z[n+2]--z[n+3]    --cycle;  endfor;  for n=0 upto 3:   xdraw() z[n]--z[n+4];  endfor;  xdraw() z0--z2;  xdraw() z0--z6; } % [G]直方体の頂点 \mptLabel{z0}[t]   <0mm,-1mm>{O} \mptLabel{z1}[t]   <0mm,-1mm>{A} \mptLabel{z2}[tl]{B} \mptLabel{z3}[t]   <0mm,-1mm>{C} \mptLabel{z4}[bl]   <1mm,1mm>{D} \mptLabel{z5}[br]{E} \mptLabel{z6}[b]   <0mm,1mm>{F} \mptLabel{z7}[bl]{G} \end{MPpic}
[A]で座標軸を定義した後で、 [B]では最初に空間座標を表す変数宣言を しています。「color c[];」という形で宣言すると、その後で c0,c1などとして 利用することができます。これは、c[0], c[1] と同じ事です。 宣言の段階で「color c0, c1」などとするとエラーになるので注意が必要です。

[C]では、e0を原点に、e1, e2, e3をそれぞれ座標軸方向の単位ベクトルとして 定義しています。e1, e2などに直さず、いきなり red, green, blueなどを 利用してもかまいませんが、わかりやすさの観点で置き換えています。

[D]では、個々の頂点をベクトルの終点として定義しています。 c0〜c7は、それぞれ頂点O〜Gに対応しています。 たとえば、c1は \(\small \overrightarrow{\rm OA}\) のことです。 ベクトルの和の形で定義することができます。 ここで定義された座標は、頂点の空間座標です。

[E]では、個々の頂点を xdraw()で利用できるように proj を利用して 平面に射影した点の座標を求めています。そこでは、「w」の単位つきです。 単位をつけないと「pt」になって原点付近にかたまってしまいます。 座標の変数を番号付けておくと、 for-endfor を利用して一気に変換することができます。 初字が「z」で始まるz[]は、最初にpair宣言をしなくても利用することができます。

[F]では、個々の頂点を xdraw() で結んで直方体を描いています。 個別に結んでもよいですが、 上面と下面の長方形と4本の縦線を描く箇所を、 for-endfor を利用して省力化しています。 「for n=a,b,c:」とすると、n の値は a, b, c の値で実行されます。 「for n=a upto b:」とすると、aからbまで1ずつ増やしながら実行します。 「for n=a step b until c」とすると、aからb刻みでcになるまで実行します。

最後の[G]では、各頂点にラベルを 配置しています。 ここで利用する座標は、射影された座標であることに注意してください。


▲戻る(トップメニューマップ)

円柱座標
空間座標として、直交座標ばかりではなく円柱座標を利用することもできます。 円柱座標は、\(\small (x,y)\) の部分を極座標で考えたもので、 \(\small (r, \theta, z)\) の形の座標です。 直交座標では \(\small (r\cos\theta, r\sin\theta, z)\) になります。 3つの成分で表される (a,b,c) が円柱座標であることを示すため、 「rdh(a,b,c)」の形で書きます。 「r」は半径、「d」は角度、「h」は高さのことで、 角度は度数法を利用します。 弧度法で考えたいときは「rrh(a,b,c)」とします。

前述の直方体を、個々の頂点を円柱座標で指定して描くと、 対角線は「OF=5」で、角度は \(\small \tan^{-1}(4/3)\fallingdotseq 53^{\circ}\) であることから 下記のようになります。頂点の座標の指定を円柱座標にしただけです。

  • \begin{MPpic}<1cm>(4,4) % 座標軸 (上記と同じ) % 直方体の頂点 \mptPoint{z.O}[O][t]  <0mm,-1mm>  {proj(rdh(0,0,0))} \mptPoint{z.A}[A][t]  <0mm,-1mm>  {proj(rdh(3w,0,0))} \mptPoint{z.B}[B][tl]  {proj(rdh(5w,53,0))} \mptPoint{z.C}[C][t]  <0mm,-1mm>  {proj(rdh(4w,90,0))} \mptPoint{z.D}[D][bl]  <1mm,1mm>  {proj(rdh(0,0,2w))} \mptPoint{z.E}[E][br]  {proj(3w,0,2w)} \mptPoint{z.F}[F][b]  <0mm,1mm>  {proj(rdh(5w,53,2w))} \mptPoint{z.G}[G][bl]  {proj(rdh(4w,90,2w))} % 直方体の描画 \sendMP{  (上記と同じ) } \end{MPpic}
▲戻る(トップメニューマップ)

四面体
平面図形では三角形の3辺の比を指定して三角形を描く マクロがありましたが、 空間図形の場合は四面体の6辺の長さ(単位つき)を指定して 頂点を割り当てるマクロが用意されています。「SetTetra」です。 底面の三角形をABCとし、もう一つの頂点をPとするとき、 頂点の名前と対応する辺の長さを実寸で指定して、 \sendMP内で次のような書式で利用します。

  SetTetra(頂点の名前)
   (BC,CA,AB,PA,PB,PC)

下記は、MePoTeXのマニュアルの図56(p.43)を改変したものです。
  • \begin{MPpic}<1cm>(4|4,4) % 座標軸 \mptXTaxis|0w==6w>[r]   <1mm,0mm>{$x$} \mptYTaxis|0w==4w>[r]   <1mm,0mm>{$y$} \mptZTaxis|0w==3w>[b]   <1mm,0mm>{$z$} % 四面体の設定 \sendMP{ \SetTetra(c)  (4w,5w,4w,4w,6w,5w);} % 頂点の定義 \mptPoint{z.P}[P][b]   <0mm,1mm>{proj(c0)} \mptPoint{z.A}[A][t]   <0mm,-1mm>{proj(c1)} \mptPoint{z.B}[B][t]   <0mm,-1mm>{proj(c2)} \mptPoint{z.C}[C][l]   <1mm,0mm>{proj(c3)} % 四面体の描画 \sendMP{ xdraw() z.P--z.A--z.B   --z.C--cycle; xdraw() z.A--z.C; xdraw() z.P--z.B; xdraw() z.P--z.C; } \end{MPpic}
上記では、頂点を納めた変数をまとめて c としています。 対応する頂点 P, A, B, C の座標は、 それぞれ c0, c1, c2, c3 に納められます。 長さを与えると各頂点の座標のみたす 連立方程式が得られるので、 それを解くことで具体的な座標が求められています。 指定した長さの四面体が存在しないときはエラーが表示されます。 その頂点を得た後で、 それぞれの点を射影した点に頂点の名前を配置しています。 したがって、「z.A」などは空間座標ではなくて、 射影後の平面上の座標です。

また、頂点の配置の仕方にはいくつかの制約があります。 まず、面の一つは \(\small xy\) 平面上にあります。 その平面上の頂点の一つは原点と \(\small x\) 軸の正の部分にあり、 もう一つの頂点は \(\small y>0\) であるように、そして 平面上にはない頂点は \(\small z>0\) となるように設定されます。 また、頂点の名前は、c0は底面上でない頂点、c1は原点、c2は \(\small x\) 軸 上の頂点、そして c3は残りの頂点です。 得られた四面体は、平行移動や直線に関する回転をさせて自由に 変形することができます。

■垂線の足
平面の場合は、点から直線に下ろした垂線の足を求める コマンド「asi」が ありました。空間でも同様のコマンドがあります。「Asi」です。 「c4=Asi(P,A,B,C)」とすると、点Pから3点A, B, C を含む平面に下ろした 垂線の足(空間座標)が c4 に入ります。最後の点「C」を省略すると、 点Pから2点A, B を通る直線に下ろした垂線の足になります。 なお、図の上に書き込むには、 「proj(c4)」として射影して利用する必要があります。

下図では、前述と同じ状況で、 点Cから直線PB上に下ろした垂線の足Hと 直角記号を書き入れています。 PA, AC は破線にしました。 また、SetTetraの辺の長さを指定する引数の順番を書き入れました。

  • \begin{MPpic}<1cm>(4|4,4) % % (前と同様) % % 四面体の描画 \sendMP{  c4=Asi(c3,c0,c2);  z.H=proj(c4);  xdraw() z.C--z.H;  xdraw() z.P--z.B    --z.C--cycle;  xdraw(hasen()) z.P--z.A;  xdraw(hasen()) z.A--z.C; } \mptPoint{z.H}[H][br]{z.H} \mptRightAngleMark   {z.C}{z.H}{z.P} \mptLabel{1/2[z.B,z.C]}{1} \mptLabel{1/2[z.C,z.A]}{2} \mptLabel{1/2[z.A,z.B]}{3} \mptLabel{1/2[z.P,z.A]}{4} \mptLabel{3/4[z.P,z.B]}{5} \mptLabel{1/2[z.P,z.C]}{6} \end{MPpic}
「Asi」の引数と出力は、いずれも空間座標です。 z.Pなどは平面に射影された点の座標です。 引数を与えるときや、得られた点の扱いには注意が必要です。 辺に引数の順番を書き込むときは、PB以外は 2点間の中点を指定しています。
▲戻る(トップメニューマップ)

法線方向・上方向
ここまでは、空間の3つの座標がすべて正の領域で図形を 表示してきました。MePoTeXのデフォルトでは、 視点は、緯度10度、経度30度の点と原点を結ぶ方向から視るように 設定されています。その角度は自由に変えることができます。 それを行うのが「SetProjDir」というマクロです。 「SetProjDir(緯度, 経度)(上方向)」を\sendMP内で指定します。 \(\small z\) 軸の正の方向を上方向とするとき、 \(\small xy\) 平面は緯度0度、\(\small zx\) 平面の \(\small x>0\) の部分が経度0度です。 「上方向」は、描画するときの上方向を定める空間ベクトルです。

デフォルトでは「SetProjDir(10,30)(blue)」が指定されています。 「blue」は、(0,0,1)の値がデフォルトで設定されています。 MetaPostでは、色の指定は 0〜1 の範囲の数の3つの組で指定します。 (0,0,1)は、10進のRGB形式では(0,0,255)という青色です。 このような3つの数の組は「color」宣言することで利用でき、 0〜1 の範囲に限らないで利用すると 空間座標や空間ベクトルとして利用することができます。 ここでは、上方向のベクトルに「blue=(0,0,1)」を指定しています。 要するに、「上方向」として \(\small z\) 軸方向の正の方向を 指定したことになります。 なお、\(\small x\) 軸方向は「red=(1,0,0)」、 \(\small y\) 軸方向は「green=(0,1,0)」です。 つまり、このような形で、 任意の方向を「上方向」に指定できることになります。

  • \begin{MPpic}<1cm>(4|4,4|4) % 格子 \mptDrawGrid  [linetype=dashed hasen()]  {-4w step 1w until 4w}  {-4w step 1w until 4w} % 緯度・経度・上方向 \sendMP{  SetProjDir(10,30)(blue) } % 座標軸 \mptXTaxis|0w==5w>[r]   <1mm,0mm>{$x$} \mptYTaxis|0w==4w>[r]   <1mm,0mm>{$y$} \mptZTaxis|0w==3w>[b]   <1mm,0mm>{$z$} \end{MPpic}
上記では、参考までに格子も描画しました。 この方向でかまわない場合は、「SetProjDir」は省略してかまいません。 「SetProjDir」の意味を把握するには、 緯度・経度や上方向のベクトルをいろいろ変えてみて、 どのように表示されるか確かめてみるとよいでしょう。

以下に、座標軸がどのように見えるか、幾つかの例を示します。 なお、座標軸のラベルを「\LARGE」で大きくしています。

  • SetProjDir(-10,30)(blue)
    これは,\(\small z\) 軸を向こう側に押して \(\small xy\) 平面を 下から見上げる形になります.
  • SetProjDir(0,30)(blue)
    これは,\(\small xy\) 平面を真横から眺める形になります. 経度 0度は \(\small xz\) 平面の \(\small x\) が正の方向で, その平面が手前にまっすぐ伸びる感じになるので、 経度30度の方向から見ると \(\small x\) 軸が見える形で表示されます.
  • SetProjDir(10,0)(blue)
    経度が 0度なので、\(\small xy\) 平面の \(\small x\) が正の方向が 手前にまっすぐ伸びていますが、緯度10度の方向からみるので \(\small x\) 軸が 少し見えています。
  • SetProjDir(10,30)(green)
    green=(0,1,0) なので,これは \(\small y\) 軸を上方向とした場合です。
▲戻る(トップメニューマップ)

視点を緯度と経度で指定するのは、慣れないとちょっと分かりにくいかもしれません。 「SetProjDir」は、視点の方向を3次元ベクトルを用いて指定することもできます。 たとえば、「SetProjDir(a,b,c)(blue)」とすると、 \(\small z\) 軸の正の方向を上向きとするとき、 3次元ベクトル \(\small (a,b,c)\) の方向から視ることを指定しています。 座標平面に射影した図を見たいときは、この3次元ベクトルで指定した方が 分かりやすいかもしれません。
  • \begin{MPpic}<1cm>(4|4,4|4) % 法線方向・上方向 \sendMP{  SetProjDir(1,1,1)(blue)} % 座標軸 \mptXTaxis|0w==4w>[r]   <1mm,0mm>{$x$} \mptYTaxis|0w==4w>[r]   <1mm,0mm>{$y$} \mptZTaxis|0w==4w>[b]   <1mm,0mm>{$z$} \end{MPpic}
上記では、\(\small (1,1,1)\) の方向から視ています。 以下に、視る方向を変えて、図だけを示します。
  • \(\small (2,0,1)\) の方向から視た場合。
  • \(\small (0,2,1)\) の方向から視た場合。
  • \(\small (1,1,10)\) の方向から視た場合。

▲戻る(トップメニューマップ)

■曲線・曲面の描画

(注) Web上での表示の都合上、 空白は全角を使用しています。 以下のコードをコピーして実行するときは、 半角の空白かtabで置き換えて実行してください。


空間曲線
空間曲線は、\(\small t\) を媒介変数として、 \(\small (x,y,z)=(f(t),g(t),h(t))\) の形で表されます。 平面曲線の場合と同様に、 空間曲線は「kansuQ」により描画することができます。 下図は、半径2、高さ4の円柱に巻き付く弦巻線を描いています。 巻き付く様子が分かるように、\(\small z\) 軸を40度傾けています。
  • \begin{MPpic}<1cm>(4|4,4|4) \sendMP{ SetProjDir(40,30)(blue); % 底面と上面 xdraw() kansuQ(rdh(2,t,4))   (0,360,4w); xdraw() kansuQ(rdh(2,t,0))   (-60,120,4w); xdraw(hasen())   kansuQ(rdh(2,t,0))   (120,300,4w); % 円柱の外側の縦線 for theta=-50,120: xdraw() proj(rdh(2w,theta,0))   --proj(rdh(2w,theta,4w)); endfor; % 弦巻線 xdraw(1pt)   kansuQ(rdh(2,t,t/90))   (0,120,60); xdraw(hasen())   kansuQ(rdh(2,t,t/90))   (120,300,90); xdraw(1pt)   kansuQ(rdh(2,t,t/90))   (300,360,30); } \end{MPpic}
弦巻線は、媒介変数表示では \(\small (r\cos t, r\sin t, ar)\) ですが、 円柱座標を利用すると「rdh(\(\small r, t, ar\))」ですみます。 裏側の見えない箇所は破線にしましたが、これは手動で行いました。 曲面描画では隠線処理が行われますが、 このような場合は手動で範囲を分けるしかないようです。 また、単位をつける必要のある箇所と、つけなくてもよい箇所、 さらに「proj」で射影する必要のある箇所と、しなくてもよい箇所が 混在するので、ちょっと混乱するかもしれません。 「xdraw()」で線を引くときは、単位つきの座標を射影する必要があります。 「kansuQ」で描画するときは、単位も射影も不要です。

▲戻る(トップメニューマップ)

曲面の描画
媒介変数表示で表された曲面を描画するマクロは、 文字通りの「drawSurface」です。 2つの媒介変数 \(\small s, t\) を用いて \(\small x=f(s,t), y=g(s,t), z=h(s,t)\) と表されるとき、

 drawSurface(f(s,t),g(s,t),h(s,t))
  (sの始点,終点,分割数)
  (tの始点,終点,分割数)

の形で使用します。xdraw()を通さずに、このコマンドだけで 描画されます。 \(\small z=f(x,y)\) のグラフであれば、媒介変数で \(\small (s, t, f(s,t))\) として描画します。
  • \begin{MPpic}<.5cm>(4|4,4|4) \sendMP{  setunitlength(w,w,0.3w);  SetProjDir(20,60)(blue);  SetPolyBoth;  drawSurface(s,t,s*s-t*t)   (-4,4,10)(-4,4,10); } \mptXTaxis|0w==4w>  <1mm,0mm>{$x$} \mptYTaxis|0w==4w>  <1mm,0mm>{$y$} \mptZTaxis|0w==4w>  <0mm,1mm>{$z$} \end{MPpic}
これは、MePoTeXのマニュアル[1]の 図86(p.56)にあるものです。 「setunitlength」では、3つの方向の単位を別々に決めることができます。 特に指定されていなければ 「(w,w,w)」で設定されます。 曲面の表と裏を別々に描いています。 裏表の判定は「フレミングの法則」にならい、 親指の向きを \(\small s\) の増加方向、 人差し指の向きを \(\small t\) の増加方向とするとき、 中指の向きがSetProjDirで指定した方向から見て手前に迫ってくる側が表、 奥に遠ざかる面が裏として判定されます(p.132)。

★マニュアルの図のコードをコピーして実行して「\dis」に関する エラーが出るときはこちらを参照してください。

表側や裏側の描画に関しては、次のようなマクロが用意されています。

  • SetPolySolid:表側をグラデーションで塗り、裏側は描かない。 特に指定しなければ、これがデフォルトで設定されています。
  • SetPolyFrame:裏側の辺を破線で描いて、表側の面は塗らない。
  • SetPolyOmote:表側の面のみ枠線を描く
  • SetPolyUra:裏側の面のみを枠線を描く
  • SetPolyBoth:上記の2つを合わせた形で描く。

裏表がはっきり分かるように濃淡をつけるには、たとえば 「background:=0.7*white;」 を描画前に設定すると表側が濃くなりますが、 背景の地の色に対する指定なので座標軸のラベルの背景も同じ色になります。 「white」だけでなく、いろいろな色で試してみるとよいでしょう。 下図は、「SetPolyBoth;」を指定した上で、 backgroundの指定を行って描画したものです。


▲戻る(トップメニューマップ)

■幾つかの曲面
以下に、幾つかの描画例を掲げておきます。 gnuplotなどの 描画専用ソフトとの優劣を比較することに意味はありません。 それらのソフトを利用しなくても、TeXの中だけでここまで描画できることの ありがたみを感じるべきでしょう。 複数曲面の重ね合わせには対応していません。 以下では、座標軸部分のコードは省略しました。 長さは、曲面の状況をみながら変えています。
  • 放物面 \(\small z=4-(x^2+y^2)\)
    円柱座標で描画しました。
    • \begin{MPpic}<1cm>(4|4,4|4) \sendMP{  drawSurface   (rdh(s,t,4-s**2))   (0,2,10)(0,360,36);} (中略) \end{MPpic}
  • 円錐 \(\small z=\sqrt{x^2+y^2}\)
    隠線処理がうまく効かない場合があるようです。 実際の描画は、分割された点を「..」で繋いで xdraw() により 描画されています。「..」で繋ぐと3次のベジェー曲線で描こうとするので、 特異性のある点は丸っこくなってしまいます。
    • \begin{MPpic}<1cm>(4|4,4|4) \sendMP{  SetPolyBoth;  drawSurface   (s,t,sqrt(s**2+t**2))   (-2,2,20)(-2,2,20);} (中略) \end{MPpic}
  • 半球 \(\small z=\sqrt{4-(x^2+y^2)}\)
    球座標で描画しました。
    • \begin{MPpic}<1cm>(4|4,4|4) \sendMP{  drawSurface   (2*sind(s)*cosd(t),    2*sind(s)*sind(t),    2*cosd(s))   (0,90,10)(0,360,18);} (中略) \end{MPpic}
  • 半円柱 \(\small z=\sqrt{4-x^2}\)
    • \begin{MPpic}<1cm>(4|4,4|4) \sendMP{  SetPolyBoth;  drawSurface   (s,t,sqrt(4-s*s))   (-2,2,10)(-2,2,10);} (中略) \end{MPpic}

▲戻る(トップメニューマップ)

  • 平面 \(\small z=2-y\)
    \(\small x, y\) の範囲を工夫する必要があります。
    • \begin{MPpic}<1cm>(4|4,4|4) \sendMP{  drawSurface(s,t,2-t)   (0,4,10)(0,2,10);} (中略) \end{MPpic}
  • \(\small z=e^{-(x^2+y^2)}\)
    • \begin{MPpic}<1cm>(4|4,4|4) \sendMP{  setunitlength(w,w,1.5w);  drawSurface(s,t,   exp(-(s*s+t*t)))   (-2,2,10)(-2,2,20);} (中略) \end{MPpic}
(注) MetaPostの計算の優先順位によると、 「-s**2」とすると \(\small (-s)^2\) と 理解されるので注意が必要です。
特異点を持つ場合は、分割数や、軸の縮尺や範囲を工夫する必要があります。
  • \(\small z=\dfrac{x^2-y^2}{x^2+y^2}\)
    • \begin{MPpic}<1cm>(4|4,4|4) \sendMP{  setunitlength(w,w,2w);  SetProjDir(1,2,1)(blue);  SetPolyBoth;  drawSurface(s,t,   (s*s-t*t)/(s*s+t*t))   (-4,4,29)(-4,4,29);} (中略) \end{MPpic}
  • \(\small z=\dfrac1{x^2+y^2}\)
    下記は、円柱座標で描画しています。
    • \begin{MPpic}<1cm>(4|4,4|4) \sendMP{  setunitlength(w,w,.5w);  drawSurface   (rdh(s,t,1/(s**2)))   (.4,2,10)(0,360,18);} (中略) \end{MPpic}

▲戻る(トップメニューマップ)

■球面調和関数
掲示板[8]で質問を受けたことで、球面調和関数の概要について知ることができました。 この関数は、ラプラシアン\(\small \Delta=\frac{\partial^2}{\partial x^2}+\frac{\partial^2}{\partial y^2}+\frac{\partial^2}{\partial z^2}\)を球座標 \(\small (r,\theta,\phi)\) で考えたときの 角度成分 \(\small (\theta,\phi)\) に関する固有関数になっており、 物理・工学の分野のみならず、日常の気象予報やゲームグラフィックス等の3次元CG、 さらには天文学、医学、音響分野等、多様な分野で利用される重要な関数です (参照)。 具体的には次の式で表されます。 \[\begin{align*} \small Y_l^m(\theta,\phi) &\small =(-1)^{\frac{m+|m|}{2}}\\ &\small  \times \sqrt{\frac{2l+1}{4\pi}\frac{(l-|m|)!}{(l+|m|)!}}\\ &\small  \times P_l^{|m|}(\cos\theta)e^{im\phi} \end{align*} \] ここで、\(\small P_l^m(x)\) はルジャンドル培関数と呼ばれる特殊関数で、 次の式で定義されます。 \[\small P_l^m(x)=\dfrac1{2^ll!}(1-x^2)^{\frac{m}{2}}\frac{d^{l+m}}{dx^{l+m}}(x^2-1)^l\] 関数の定義自体が高度で通常の数学教育にはなじまないのですが、 いろいろな専門分野では必須の関数になっています。 Wikipediaに、\(\small 0\le l\le 10\) の場合の具体的な式があります。
なお,球面調和関数について, Maximaを利用した解説は「こちら」をみてください.また, この関数の概要について第21回グラフ電卓研究会 (福井高専,2021.11)で発表しました[レジュメ].

球面調和関数のグラフとして例示されることが多いのは \(\small Y_2^0\) のグラフで、球座標では \[\small Y_2^0=\dfrac14\sqrt{\dfrac5{\pi}}(3\cos^2\theta-1)\] と表され、次のようなグラフです。 このような、球面調和関数のグラフを描画できるアプリがあります(参照)。


このグラフをMePoTeXで描いてみましょう。 \(\small y=3\cos^2{x}-1\) のグラフは下図のようになります。 \(\small x\) 軸との最初の交点は \(\small \cos{x}=\frac1{\sqrt{3}}\) より, \(\small a=\cos^{-1}\frac1{\sqrt{3}}=0.9553({\rm rad})=54.7^{\circ}\)です。 このグラフを、 横軸を \(\small \theta\)、縦軸を \(\small r\) として極座標に読み替えて 表示したのが下段のグラフです。 原点を通るのは、\(\small \theta=a, \pi-a\) のときです。 曲線上の動きとしては、 \(\small \theta\) が増えるにつれ、点は右端から左方向に動いていきます。 このグラフを横軸の回りに回転したものが \(\small Y_2^0\) のグラフです。
  • \begin{MPpic}<1cm>(3.5,3|2) \mptDrawGrid  [linetype=dashed hasen()]  {}{-1w step 1w until 2w} \mptXaxis[l]<1mm,0mm>{$x$} \mptYaxis[b]<0mm,1mm>{$y$} \mptDrawTick  {}{-1h step 1h until 2h} \mptCoord(0,-1)(0,1){4}  [r]<-1mm,0mm>{\ycoord} \mptLabel{(3.14w,0)}  [t]<0mm,-1mm>{$\pi$} \mptLabel{(1.57w,0)}  [b]<0mm,1mm>  {$\frac{\pi}{2}$} \sendMP{ xdraw() kansu(  3*(cos(t)**2)-1)(0,PI,20); xdraw(1pt,tensen())  Pxline(PI*w,2h); xdraw(1pt,tensen())  Pxline(PI*w/2,-h);} \end{MPpic}
  • \begin{MPpic}<1cm>   (2.5|2.5,1.5|1.5) \mptXaxis[l]<1mm,0mm>{$x$} \mptYaxis[b]<0mm,1mm>{$y$} \sendMP{ xdraw() kansuP(  (3*(cos(t)**2)-1)*cos(t),  (3*(cos(t)**2)-1)*sin(t))  (0,PI,40);} \mptLabel{(2w,0)}[t]   <0mm,-1mm>{$2$} \mptLabel{(-2w,0)}[t]   <0mm,-1mm>{$-2$} \end{MPpic}

球座標で定義された式を直交座標の媒介変数での表示に直して描画すると、 下記のようになります。
  • \begin{MPpic}<10cm>(.4|.4,.4|.4) \sendMP{  vardef r(expr s)=   1/4*sqrt(5/PI)*(3*(cos(s)**2)-1)  enddef;  drawSurface   (r(s)*sin(s)*cos(t),    r(s)*sin(s)*sin(t),    r(s)*cos(s))   (0,PI,20)(0,2*PI,40);} \end{MPpic}
雰囲気は似ていますが、何やらおかしいです! これは、MePoTeXは、曲面の裏表の判断を簡易仕様で作成しているためのようです。 これをクリアするには、裏表の判定がしやすいように、 幾つかのパーツに分けて奥から順に描画するとよいようです。 (赤青の)グラフを奥にあるものから順に分けると、 「輪の向こう側」「上と下の突起部分」、 そして一番手前は「輪の手前部分」です。 輪の部分は、\(\small a=\cos^{-1}\frac1{\sqrt{3}}\) とするとき、 \(\small a\leq \theta\le \pi-a\) の部分です。 曲面の裏表の判断はマニュアル[1]のp132に書かれています。 輪の部分が問題で、 手前に見える表側は、向こう側では裏側になっています。 表と裏が反対になっているので、 曲面の表裏の判断を「反対に判断させる」必要があります。 その指定は、定義域の範囲を逆に指定することで指示することができるようです。 つまり、下記のコードで、正しく描画されます。
  • \begin{MPpic}<10cm>(.4|.4,.4|.4) \sendMP{%  numeric a; a:=Arccos(1/sqrt3);  SetProjDir(10,-90)(blue);  vardef r(expr s)=    1/4sqrt(5/PI)*(3cos(s)*cos(s)-1)  enddef;  drawSurface(r(s)*sin(s)*cos(t),   r(s)*sin(s)*sin(t),r(s)*cos(s))   (PI-a,a,20)(PI,2PI,40);  drawSurface(r(s)*sin(s)*cos(t),   r(s)*sin(s)*sin(t),r(s)*cos(s))   (0,a,20)(0,2PI,40);  drawSurface(r(s)*sin(s)*cos(t),   r(s)*sin(s)*sin(t),r(s)*cos(s))   (PI-a,PI,20)(0,2PI,40);  drawSurface(r(s)*sin(s)*cos(t),   r(s)*sin(s)*sin(t),r(s)*cos(s))   (PI-a,a,20)(0,PI,40); } \end{MPpic}
Expi(緯度,経度)
いちいち媒介変数表示で指定するのは面倒です. MePoTeXには,空間の緯度(s)・経度(t)を指定して, その方向の単位ベクトルを返す「Expi(s,t)」というコマンドがあります. ただし,緯度0は赤道平面なので, $\small z$軸からの角を$\small s~(0\le s\le \pi)$とした場合は $\small \pi/2$だけずらしてExpi(PI/2-s,t)とする必要があります. これを利用すると,上記の3つの座標を指定する部分は 単に「r(s)*Expi(PI/2-s,t)」とするだけで済みます. たとえば,2番目の「drawSurface」は 「drawSurface(r(s)*Expi(PI/2-s,t))(0,a,20)(0,2PI,40);」 となり簡潔に記述できます.度数法の場合は「Dir(s,t)」を利用します.

この記法を用いて$\small Y_3^0$のグラフを描画してみましょう. \[\small Y_3^0=\dfrac14\sqrt{\dfrac{7}{\pi}}(5\cos^2\theta-3)\cos\theta\] 次のように,下から描いていくことになります.

  • \begin{MPpic}<10cm>  (.4|.4,.4|.4) \mptNumSys{double} \sendMP{% numeric a[]; a1=Arccos(sqrt(3/5)); a2=PI/2;a3=PI-a1; SetProjDir(10,-90)(blue); vardef r(expr s)=1/4sqrt(7/PI)  *abs(cos(s)*(5cos(s)*cos(s)-3)) enddef; drawSurface(r(s)*Expi(PI/2-s,t))  (a3,PI,10)(0,2PI,40); drawSurface(r(s)*Expi(PI/2-s,t))  (a2,a3,10)(0,PI,40); drawSurface(r(s)*Expi(PI/2-s,t))  (a2,a3,10)(PI,2PI,40); drawSurface(r(s)*Expi(PI/2-s,t))  (a1,a2,10)(0,PI,40); drawSurface(r(s)*Expi(PI/2-s,t))  (0,a1,10)(0,2PI,40); drawSurface(r(s)*Expi(PI/2-s,t))  (a1,a2,10)(PI,2PI,40);} \end{MPpic}

【注】 このような曲面を描画するとき, 幾つかのパーツに分けて奥から順に描画するとよいこと, そして裏表の判断を逆にするには定義域の数値の大小を逆にすればよいこと, ならびに「Expi」を利用すると簡潔に記述できることは, いずれも「みなも」さんにご教示いただきました.

▲戻る(トップメニューマップ)

■基本的な空間図形

(注) Web上での表示の都合上、 空白は全角を使用しています。 以下のコードをコピーして実行するときは、 半角の空白かtabで置き換えて実行してください。


中心と半径を与えて、グラデーションで塗りつぶされた球を描くことができます。 「mptBall(中心, 半径)(色変化)」の形で指定します。 実際には、円をグラデーションで塗りつぶして球のようにみせているだけのようです。 球の中心は空間座標ですが、 射影して描画しているので中心の座標は2成分である必要があります。 空間座標で指定するときは、それを「proj」で射影して 使用してください。

「色変化」は、パラメータを \(\small t~(-1\leqq t\leqq 1)\) として、 濃い・薄いの濃度変化を表す式を定義します。 マニュアル[1](p.122)では 次の設定例が紹介されています。 「white」以外の色で試してみてください。

  • 「1/2(1+t)*white」とすると、黒から白まで均等に変化する。
  • 「1/4(1+t)*(1+t)*white」とすると、黒っぽい箇所が多くなる。
  • 「0.1(7+3t)*white」とすると、もっとも暗いところでも 0.4whiteになる。
  • \begin{MPpic}<5mm>  (4|4,4|4) \sendMP{  mptBall((0,0),3w)   (1/2(1+t)*white); } \end{MPpic}
以下は、「drawSurface」を利用した場合です。
  • \begin{MPpic}<5mm>  (4|4,4|4) \sendMP{  drawSurface   (2*sind(s)*cosd(t),   2*sind(s)*sind(t),   2*cosd(s))   (0,180,9)(0,360,18);} \end{MPpic}

▲戻る(トップメニューマップ)

柱体・錐体
MePoTeXには、円錐や角錐、あるいは円錐台や角錐台を描画できる マクロが用意されています。
■円柱と角柱
最初に円柱や角柱を描いてみましょう。 描画のために必要な引数は、中心、半径、高さ、そして角数です。 使用するマクロは「mptcolumn」です。

 mptcolumn(中心)(半径, 高さ,角数)オプション

中心の座標は、\(\small xy\) 平面の単位つきの座標で、 事前に定義しておきます。引数に座標を直接書き入れるとエラーになります。 半径と高さは、いずれも単位つきで指定します。 円柱は、角数の多い角柱として描かれているので、 円柱の場合も設定する必要があります。 円柱の場合は、負数で \(\small -50\sim -100\) 程度の値で指定します。 グラデーションをつける関係で、多角柱で描画する仕様にしたようです。 オプション値は、引数を「,」で区切って5個まで指定することができ、 中心軸を傾けたり平面で切ったときの切り口を描画することができます。 詳細は、「円錐・角錐の傾斜」を見てください。
  • \begin{MPpic}   <7mm>(4|4,4|1) \sendMP{ SetProjDir(20,30)(blue); z0=(0,0); mptcolumn(z0)(2w,6h,6); } \mptXTaxis|0==4w>   <1mm,0mm>{$x$} \mptYTaxis|0==4w>   <1mm,0mm>{$y$} \mptZTaxis|0==4w>   <0mm,1mm>{$z$} \end{MPpic}
上記では、六角柱が描かれています。 表面が塗られていますが、これはデフォルトの設定である 「SetPolySolid」によるものです。 「SetPolyFrame;」を指定すると、 裏側が破線の枠線で描画されます。

円柱であれば、角数を「-50〜-100」の間で指定します。 下図は「SetPolyFrame;」を指定して、角数を「-50」としたものです。


▲戻る(トップメニューマップ)

■円錐と角錐
円錐や角錐を描くマクロは「mptcone」です。

 mptcone(中心)(半径, 高さ,角数)オプション

下図では、座標軸は省略しました。 円錐は、角数を \(\small -50\sim -100\) の数で指定することで 描画されます。
  • \begin{MPpic}   <7mm>(4|4,4|1) \sendMP{  SetProjDir(20,40)(blue);  z0=(0,0);  SetPolyFrame;  mptcone(z0)(2w,6h,6); } \end{MPpic}

▲戻る(トップメニューマップ)

■円錐・角錐の傾斜
「mptcone」の最後の引数である「オプション」には、 数値を最大5個まで「,」で区切って指定することができます。 その引数を順に「1,2,3,4,5」とすると、 通常の \(\small z\) 軸の正の方向が上側である座標系では、 それぞれ次のような内容です。
  1. 最初の引数では、中心軸の傾斜角を指定できます。 それは、中心軸を含む \(\small xz\) 平面と平行な平面内で、 中心軸をどれだけ傾けるかを指定する角度です。
  2. 2番目の引数は、傾斜させる中心軸の傾斜方向を指定します。 それは、中心軸を含む \(\small xz\) 平面と平行な平面を、 中心軸の回りにどれだけ回転するかを示す角度です。 ただし、1番目, 2番目の引数で傾けても錐体の高さは変わりません。 つまり、底面の中心点を中心に錐体を回転しているわけではありません。
  3. 3〜5番目の引数は、円錐を平面で切断したときの断面曲線を描くための引数です。 角錐には対応していません。 3番目の引数は、最大傾斜方向にある母線との切り口の交点を、 母線上の相対位置で指定します。 要するに、平面との断面で縦座標が最大になる点の、 その点を含む母線上の相対位置です。 「0」のときは下の底面、「1」のときは「上面」です。
  4. 4番目の引数は、切断する平面と底面との傾斜角です。
  5. 5番目の引数は、 3番目の引数で指定した交点を含む母線と中心軸を含む平面が、 \(\small xz\) 平面の x が正の方向の部分とのなす角度です。 指定しないときは「0」が設定され、その母線は \(\small xz\) 平面と 平行な平面上にあることが想定されます。
下記は、原点を中心とする高さ5hの四角錐を、 \(\small z\) 軸から\(\small x\) 軸方向に30度傾けた場合と、 \(\small x\) 軸から60度の方向に30度傾けた場合です。
  • \begin{MPpic}<7.5mm>   (4|4,4|4) \sendMP{  z0=origin;  SetPolyFrame; % z軸からx軸方向に30度傾けた  mptcone(z0)(2w,5h,4)30; % x軸から60度の方向に % 30度傾けた %mptcone(z0) %  (2w,5h,4)30,60; } \mptXTaxis|0==5w>   <1mm,0mm>{$x$} \mptYTaxis|0==4w>   <1mm,0mm>{$y$} \mptZTaxis|0==5w>   <0mm,1mm>{$z$} \end{MPpic}
下の図は、コメントアウトした箇所により描かれます。 ただし、頂点の位置(z0v)が2つの錐体では異なるので、 2つの図を同時に描画することはできません。

■円錐と平面との断面
下記は、円錐を底面とのなす角が30度の平面で切ったときの 断面です。
  • \begin{MPpic}<7.5mm>  (4|4,4|4) \sendMP{  z0=origin;  SetPolyFrame; % 最大傾斜点は母線の中点 % 底面とのなす角は30度  mptcone(z0)(2w,5h,-50)   0,0,0.5,30;} % 下図の場合 % mptcone(z0)(2w,5h,-50) %  0,0,0.5,30,180;} \mptXTaxis|0==5w>   <-2mm,0mm>{$x$} \mptYTaxis|0==4w>   <1mm,0mm>{$y$} \mptZTaxis|0==6w>   <0mm,1mm>{$z$} \end{MPpic}
上の図は、最大傾斜点が \(\small xz\) 平面の \(\small x\) が正の側にある場合です。 手前に断面があり、向こう側に平面が下がっていく感じです。 下の図は、最大傾斜点が \(\small x<0\) の側にある場合です。

▲戻る(トップメニューマップ)

■円錐台と角錐台
円錐台や角錐台を描くには、 底面の半径に加えて上面の半径も指定する必要があります。 それらを引数に持つマクロは「mptColumnCone」です。

 mptColumnCone(中心)(半径,高さ,角数)(緯度,経度)オプション

円柱や円錐と同じような引数のように見えますが、 「半径」の箇所が正数であれば円柱や角柱になります。 「(4w,2w)」のようなペア型変数で指定すると、 底面の半径が4w、上面の半径が2wの台になります。 「(0,2w)」や「(4w,0)」と一方に「0」が含まれると、 円錐か角錐で、前者は頂点が下にあり、後者は上にあります。
  • \begin{MPpic}   <5mm>(4|4,4|1) \sendMP{ SetProjDir(20,40)(blue); SetPolyFrame; z0=(0,0); mptColumnCone(z0)   ((4w,2w),6h,6)(0,0); } \mptLabel{z0b0}[t]{A} \mptLabel{z0b1}[t]{B} \mptLabel{z0b2}[t]{C} \mptLabel{z0t3}[b]{P} \mptLabel{z0t4}[b]{Q} \mptLabel{z0t5}[b]{R} \end{MPpic}
上記では、六角錐の台が描画されています。 頂点の名前も付しました。 中心を「z0」とした場合、角錐の底面と上面の頂点の座標は、 自動的に次の変数に割り当てられています。
  • 底面の頂点の座標は、z0b0, z0b1, z0b2, ・・・。
  • 上面の頂点の座標は、z0t0, z0t1, z0t2, ・・・。
「mptColumnCone」の3つ目の括弧を \(\small (a, b)\) とすると、 図形が \(\small y\) 軸の周りに \(\small -a\) 度、 \(\small z\) 軸の周りに \(\small b\) 度回転した図形が描画されます。 この部分は、とりあえずは「(0,0)」で指定して、図を見ながら いろいろ試してみるとよいでしょう。 表側と裏側への配慮は、この回転では反映されないようです。
  • (a,b)=(-30,0) の場合
  • (a,b)=(0,120) の場合
  • (a,b)=(60,45) の場合

▲戻る(トップメニューマップ)

■正多面体
正多面体を描くマクロもあります。 それぞれのマクロは、次のような名前です。
  1. 正四面体は「tetrahedron」
  2. 正六面体は「hexahedron」
  3. 正八面体は「octahedron」
  4. 正十二面体は「dodecahedron」
  5. 正二十面体は「icosahedron」
これらの引数は、中心と大きさです。 大きさでは、外接円の半径か、1辺の長さを指定することができます。 1辺の長さを指定するときは負の値で指定します。 以下は、正八面体の場合です。 正数なので、外接円の半径で指定しています。 「-4w」を指定すると1辺の長さになります。
  • \begin{MPpic}   <5mm>(4|4,4|4) \sendMP{ z0=(0,0); octahedron(z0)(4w); } \end{MPpic}
円錐や角錐の場合と同様に、引数を追加すると角度を変えることができます。 ただし、この引数を追加するときは、 マクロ名の初字は大文字とします。

下図は、\(\small y\) 軸の周りに\(\small -30\)度回転してから、 \(\small z\) 軸の周りに40度だけ回転したものです。 「SetPolyFrame」として、枠線だけ描画するようにしています。 頂点にラベルをつけましたが、 各頂点は中心を指定した変数に対して自動的に定まります。 一見するとわかりにくいですが、頂点「B」は手前側、 頂点「D」は奥側の点です。向こう側に倒して下から見上げる感じです。 ただし、角錐のときとは異なり、 添え字は「0」ではなく「1」から始まるので注意が必要です。

  • \begin{MPpic}   <5mm>(4|4,4|4) \sendMP{ z0=(0,0); SetPolyFrame; Octahedron(z0)(4w)(30,40); } \mptLabel{z0v1}[b]{A} \mptLabel{z0v2}[tl]{B} \mptLabel{z0v3}[l]{C} \mptLabel{z0v4}[tr]{D} \mptLabel{z0v5}[r]{E} \mptLabel{z0v6}[t]{F} \end{MPpic}

▲戻る(トップメニューマップ)


■空間図形の変形
MetaPostのデフォルトでは備わってはいませんが、 MePoTeXには空間のベクトルに下記のような変形を行うことができます。 アフィン変換を行うこともできます。

(注) Web上での表示の都合上、 空白は全角を使用しています。 以下のコードをコピーして実行するときは、 半角の空白かtabで置き換えて実行してください。


原図の直方体
ここでは、元となる図として直方体を利用して、 空間ベクトルに対するいろいろな変形を行うコマンドを紹介します。 下記の直方体を利用します。
  • \begin{MPpic} <1cm>(4|1,4|1) % [A]座標軸 \mptXTaxis|0w==4w>[r] <1mm,0mm>{$x$} \mptYTaxis|0w==4w>[l] <1mm,0mm>{$y$} \mptZTaxis|0w==2w>[b] <0mm,1mm>{$z$} \sendMP{ % [B]頂点定義  color c[];  c0=black; c1=2*red;  c3=2*green; c4=blue;  c2=c1+c3;c5=c1+c4;  c6=c2+c4;c7=c3+c4; % [C]原図(破線) for n=0 upto 7:   z[n]=w*proj(c[n]);  endfor;  for n=0,4:   xdraw(hasen()) z[n]--   z[n+1]--z[n+2]--   z[n+3]--cycle;  endfor;  for n=0 upto 3:   xdraw(hasen())   z[n]--z[n+4];  endfor;  xdraw(hasen()) z0--z2;  xdraw(hasen()) z0--z6; } \end{MPpic}
上記では、最初に[A]で座標軸を設定します。 [B]では c[] を color変数(3次元)として宣言して、 c0, c1 等に直方体の頂点の座標を定義しています。 color宣言をしないとエラーになります。 「c[]」として [] をつけることで、c0, c1 などの数字の添え字を 利用することができます。 ここでは、原点をc0 とし、底面の頂点を反時計回りに c1,c2,c3 、 上面の頂点は \(\small z\) 軸上の頂点を c4として反時計回りに c5,c6,c7としています。その定め方は、 最初に座標軸方向のベクトル c1, c3, c4 を定め、 他はそれらの和で定義しています。 blackは色の名前ですが、カラーコードでは (0,0,0) なので要するに原点です。 red=(1,0,0), green=(0,1,0), blue=(0,0,1)です。

[C]では、個々の頂点を射影(proj)して単位(w)をつけた点を z0, z1, z2に格納し、 xdraw()により長方形を破線で描画しています。 初字が「z」の場合は、変数宣言をしないで利用することができます。 最初に底面と上面の長方形を、次に縦の線を、 そして最後に対角線をいずれも破線で描画しています。


▲戻る(トップメニューマップ)

描画マクロの定義
これから、前述の直方体に種々の変形を加えて 変形後の図形を描画することになります。 元の直方体の頂点がどのような点に移されるかを求めて、 変換後の点を結んで変形後の図形を描画するだけですが、 その度に前述の[C]の描画部分を記述するのは面倒です。 そこで、ここでは、 頂点を結んで図形を描く部分をマクロ化しておきましょう。 以下では、 「MetaPostのマクロ」と 「TeXのマクロ」を紹介します。 いずれも、MePoTeXをすでに10年以上も使い続けておられ、 TeXやMetaPostのマクロに熟達された方からご教示いただいたものです。

MetaPostのマクロ
MetaPostのマクロとして作成するには、 マニュアル[3]で詳述されている MetaPostの言語仕様を把握していなければなりません。 私自身は、上記の方からご教示いただいた内容をもとに 付け焼き刃で解説しているので、 場合によっては間違えた説明をしているかもしれません。 詳細は「マニュアル[3]」の「マクロ定義」(60頁)を熟読してください。

値を返さないで、単に一連の動作を行うMetaPostのマクロを定義するには、 \sendMP内で次のように定義します。

def マクロ名(引数1)(引数2):=
  実現したい動作
enddef;

(引数)は必要な数だけ引き渡すことができます。 前述の[C]の部分で記述する動作を行うために必要なものは、 直方体の頂点の空間座標を納めている c[n]、 それを射影して得られる平面座標を納める z[n]、 そして描画する線種や色など xdraw() の括弧内に与えるオプションです。 この場合の「c」や「z」に相当するものを「suffix」といいます。 一方、xdraw() の括弧内に引き渡す「hasen()」は、 その文字をそのまま引き渡すので「text」といいます。 また、何らかの値を引き渡すときは、 その値は「expr」といいます。 つまり、マクロに引き渡される引数には、 「expr」「suffix」「text」 の3種類があり、 その都度、どのタイプの引数であるかを指定する必要があります。

以上をもとに[C]の部分を、マクロ名を「drawBrick」として 作成すると次のようになります。

  • \begin{MPpic}<1cm>(4|1,4|1) % [C'] 長方形描画マクロの定義 \sendMP{ def drawBrick(suffix z)  (suffix c)(text op):=  for n=0 upto 7:    z[n]=w*proj(c[n]);  endfor;  for n=0,4:   xdraw(op) z[n]--z[n+1]    --z[n+2]--z[n+3]    --cycle;  endfor;  for n=0 upto 3:   xdraw(op) z[n]--z[n+4];  endfor;  xdraw(op) z0--z2;  xdraw(op) z0--z6; enddef;} \end{MPpic}

▲戻る(トップメニューマップ)

これはマクロを定義しているだけなので、 実行しても何も表示されません。また、 実際に描画させるには、 事前に直方体の頂点 c[n] が定義されている必要があります。 マクロを定義しても、このマクロの内容をいちいち書き込んでいたのでは マクロの意味がありません。 \sendMP{ } の中括弧内を、たとえば「drawbrick.mp」として保存して、 利用するときは\sendMP内で 「input drawbrick.mp;」として読み込みます。 このマクロを利用して前述の直方体を描画すると、 次のようになります。
  • \begin{MPpic}<1cm>(4|1,4|1) % [A]視点と座標軸 \sendMP{  SetProjDir(20,40)(blue);} \mptXTaxis|0w==4w>[r]   <1mm,0mm>{$x$} \mptYTaxis|0w==3w>[l]   <1mm,0mm>{$y$} \mptZTaxis|0w==3w>[b]   <0mm,1mm>{$z$} % [B]長方形の頂点定義 \sendMP{  color c[];  c0=black; c1=2*red;  c3=2*green; c4=blue;  c2=c1+c3;c5=c1+c4;  c6=c2+c4;c7=c3+c4;} \sendMP{ % [C']描画マクロの読み込み  input drawbrick.mp; % [D] 原図長方形の描画  drawBrick(z)(c)(hasen());} \end{MPpic}
上記では、最初に[A]で視点をちょっと変更してみました。 座標軸は、視点を変更してから指定する必要があります。 視点変更の前に座標軸を描くと、その座標軸は デフォルトの視点である「SetProjDir(10,30)(blue);」により 描画されます。その後では、[B]で頂点を指定して、 [C']でマクロを読み込んで、それから [D]でマクロを利用した描画が行われています。

▲戻る(トップメニューマップ)

TeXによるマクロ
同じ内容を、TeXのマクロで作成することもできます。 TeXに慣れている方は、こちらの方が分かりやすいかもしれません。 このマクロも、この項目の冒頭で紹介した方からご教示いただいたものです。
\def\drawBrick#1#2#3{%
 \sendMP{
  for n=0 upto 7:
    #1[n]=w*proj(#2[n]);
  endfor;
  for n=0,4:
   xdraw(#3) #1[n]--#1[n+1]--
    #1[n+2]--#1[n+3]--cycle;
  endfor;
  for n=0 upto 3:
   xdraw(#3) #1[n]--#1[n+4];
  endfor;
  xdraw(#3) #10--#12;
  xdraw(#3) #10--#16;
  }
}
内容的には、MetaPostのマクロと同様です。 最初に、引数として #1, #2, #3 の3つ使用することを述べた後で、 その引数を利用して[C]の内容が記述されています。 マクロの{ }と、\sendMP の { } の2つの括弧を閉じることに気をつけてください。 この内容を、たとえば「drawbrick.tex」として保存しておけば、 「input "drawbrick.tex"」により読み込んで、 「\sendMP」の外側で「\drawBrick{z}{c}{hasen()}」とすれば 同じ図が得られます。 「input "drawbrick.tex"」を配置する箇所は、 プリアンブル部分、「MPpic」環境の前、あるいは その内部の「\sendMP」の外側であればどこでもかまいません。

▲戻る(トップメニューマップ)

平行移動
空間ベクトルに対して平行移動を行うコマンドは「Shifted」です。 空間ベクトル \(\small \mathbf{p}\) を平行移動する場合は、 移動の空間ベクトルを \(\small \mathbf{m}\) とするとき、 「q=p Shifted m」とします。 \(\small \mathbf{q}\) が移動後のベクトルで、 当然ながら、 \(\small \mathbf{p},\mathbf{q},\mathbf{m}\) は color宣言されている必要があります。 実際に表示するには「proj」で射影して「w」等の単位をつけて、 xdraw()などで描画することになります。
  • \begin{MPpic} <1cm>(4|1,4|1) % [A]座標軸 \sendMP{ % [B]頂点の定義 % [C'] マクロの読み込み  input drawbrick.mp; % [D] マクロで描画  drawBrick(z)(c)(hasen());} \sendMP{  color m,cc[]; pair zz[]; % [E]平行移動  m=(2,2,0);  for n=0 upto 7:   cc[n]:=c[n] Shifted m;  endfor; % [F]変換後の描画  drawBrick(zz)(cc)(); } \end{MPpic}
デフォルトの視点を利用し、 [A][B][C]はと同じです。 \sendMPは[B][C'][D]の箇所と、 変数宣言と[E][F]の箇所です。 [E]では、color変数として宣言された m, cc[], zz[]を利用して 平行移動を m=(2,2,0) とし、 cc[]には直方体の頂点 c[]を m だけ平行移動した空間座標を格納しています。 そして、[F]で作成した描画マクロ「drawBrick」を利用して変換後の図形を 描画しています。 指定通りに平行移動されているのが分かります。

▲戻る(トップメニューマップ)

座標軸中心の回転
ベクトル \(\small \mathbf{p}\) を \(\small x\) 軸中心に \(\small t\) 度回転するには「p XRotated t」とします。 他の軸も同様です。 下記は、\(\small x\) 軸中心に\(\small -45\)度回転したものです。 [A]〜[D]や[F]の描画部分は、「drawBrick」を利用する前項と同一です。
  • \begin{MPpic} <1cm>(4|1,4|1) % [A]〜[D] \sendMP{  color m,cc[]; pair zz[]; % [E] x軸中心に回転  for n=0 upto 7:   cc[n]:=c[n] XRotated -45;  endfor; % [F] 移動後の描画 } \end{MPpic}

▲戻る(トップメニューマップ)

任意軸中心の回転
空間内の点と原点を結ぶ直線に関する回転は「Rotated」です。 空間内の点は緯度と経度で指定し、回転角と合わせた 3つの成分を持つcolor値変数に設定します。 その変数をたとえば \(\small \mathbf{r}\) とすると、 「p Rotated r」によりベクトル \(\small \mathbf{p}\) が回転されます。 下図は、緯度と経度が (0,60) の点と原点を結ぶ直線の周りに30度回転した場合です。 [A]〜[D]や[F]の描画部分は、平行移動の項と同一です。
  • \begin{MPpic} <1cm>(4|1,4|1) % [A]〜[D] \sendMP{  color r,cc[]; pair zz[]; % [E] 任意軸での回転 r=(0,60,30);  for n=0 upto 7:   cc[n]:=c[n] Rotated r;  endfor; % [F] 移動後の描画 } \end{MPpic}

▲戻る(トップメニューマップ)

任意直線中心の回転
空間内の2点 \(\small \mathbf{a},\mathbf{b}\) を通る直線の周りに t度回転するには、「Rotatedaround (a,b,t)」とします。 下記は、回転がわかりやすいように、(2,0,0)と(2,3,0)を通る直線の 周りに90度回転したものです。 [A]〜[D]や[F]の描画部分は、平行移動の項と同一です。
  • \begin{MPpic} <1cm>(4|1,4|1) % [A]〜[D] \sendMP{  color a,b,cc[]; pair zz[]; % [E]任意直線での回転  a=(2,0,0); b=(2,3,0);  for n=0 upto 7:   cc[n]:=c[n]    Rotatedaround (a,b,90);  endfor; % [F] 移動後の描画 } \end{MPpic}

▲戻る(トップメニューマップ)

任意平面での対称移動
空間内の任意の3点を通る平面に関する対称移動を行うには 「Reflectedabout」を利用します。 3点を表すcolor値変数を \(\small \mathbf{a},\mathbf{b},\mathbf{c}\) とするとき、 「p Reflectedabout (a,b,c)」により \(\small \mathbf{p}\) が 対称移動されます。 下記は、移動がわかりやすいように、直方体の上面に関して 対称移動したものです。 [A]〜[D]や[F]の描画部分は、平行移動の項と同一です。
  • \begin{MPpic} <1cm>(4|1,4|1) % [A]〜[D] \sendMP{  color a,b,c,cc[];  pair zz[]; % [E]平面での対称移動  a=(0,0,1); b=(2,0,1);  c=(0,3,1);  for n=0 upto 7:   cc[n]:=c[n]   Reflectedabout (a,b,c);  endfor; % [F] 移動後の描画 } \end{MPpic}

▲戻る(トップメニューマップ)

アフィン変換
アフィン変換は、\(\small \mathbf{x}, \mathbf{x'}, \mathbf{p}\) を 列ベクトルとして \(\small A\) を同じ次数の正方行列とすると \[\small \mathbf{x'}=A\mathbf{x}+\mathbf{p}\] で表される変換です。この変換は、平面や空間の場合は、 点や直線に関する対称移動や、回転あるいは平行移動を含む変換です。 平面の場合のアフィン変換はMetaPostの機能に含まれていますが、 空間の場合の変換を行う機能はありません。MePoTeXでは、 空間の場合のアフィン変換を行うマクロが用意されています。 以下では、座標平面に平行な平面内でのアフィン変換と、 空間におけるアフィン変換について解説します。

■座標平面と平行な平面におけるアフィン変換
平面のアフィン変換を行うときに求めたtransform値は、 座標平面に平行な平面内のアフィン変換としても利用することができます。 座標平面は、xy、yz、zx 平面の3種類ありますが、 それらの平面に平行な平面におけるアフィン変換は、 それぞれ「XTTransformed」「YZTransformed」「ZXTransformed」です。 たとえば \(\small yz\) 平面に平行な平面での変換は 「q := p YZTransformed t」とします。 これにより、その平面内で 空間ベクトル \(\small \mathbf{p}\) が 空間ベクトル \(\small \mathbf{q}\) に変換されます。 最初の3文字は大文字です。t は、 平面内のアフィン変換 であらかじめ定めておく必要があります。 平面でのアフィン変換では p, q としてパス値(経路)も使用できましたが、 空間での変換では color値で宣言されている必要があります。 経路に適用することはできず、適用できるのは空間ベクトルだけである ことに注意してください。

今、平面でのアフィン変換を \[\small \begin{pmatrix}x'\\ y'\end{pmatrix} =\begin{pmatrix}a&b\\ c&d\end{pmatrix} \begin{pmatrix}x\\ y\end{pmatrix} +\begin{pmatrix}e\\ f\end{pmatrix}\] とすると、たとえば \(\small yz\) 平面と平行な平面 \(\small x=r\) に おけるアフィン変換は \[\small \begin{align*} &\begin{pmatrix}r\\q_1\\ q_2\end{pmatrix} =\\ &\begin{pmatrix}1&0&0\\0&a&b\\0&c&d\end{pmatrix} \begin{pmatrix}r\\p_1\\ p_2\end{pmatrix} +\begin{pmatrix}0\\e\\ f\end{pmatrix} \end{align*}\] で定まる変換です。

ここで、「YZTransformed」を利用した変換を 「原図の長方形」に適用してみましょう。 最初に、そのようは変換を行う transform値を設定します。 例として、平面で \(\small x\) 方向(横方向)に1/2倍、 \(\small y\)方向(縦方向)に2倍して、 それを \(\small (2,1)\) まで平行移動する場合を考えます。 下図は、それを \(\small yz\) 平面やそれと 平行な平面 \(\small x=1\) に対して適用した場合です。

  • \begin{MPpic} <1cm>(4|1,4|1) % [A]〜[D] \sendMP{ % [E] transform値と変換 color cc[]; pair zz[]; transform t; t=identity xscaled 1/2 yscaled 2 shifted (2,1); for n=0 upto 7: cc[n]:= c[n] YZTransformed t; endfor; % [F] 移動後の描画 } \end{MPpic}
上記では、下記のことに留意してください。
  • [A]〜[D]は、前項と同様です。
  • ここでは \(\small xy\) 平面で定義した transform値を利用しています。 \(\small yz\) 平面での横方向は \(\small y\) 方向で、 \(\small y\) 方向は \(\small z\) 方向であることに留意してください。 \(\small (2,1)\) 方向への平行移動は、\(\small y\) 方向に 2、 \(\small z\) 方向に 1だけ平行移動したことになります。
  • [E]では、最初に transform値として宣言した「t」を定めています。 ここでの留意事項は、平行移動や拡大・縮小を指定する場合に、 「単位 w, h はつけない!」ということです。 それは、これらの単位を変換後の図形を描画するマクロ「drawBrick」で つけるようにしているからです。したがって、 transform値を対応する点の対応関係で定義するときは、 単位をつけない座標で定義する必要があります。 単位をつけて定義すると、 描画マクロ「drawBrick」を利用するときも単位「w」がつくので、 要するに \(\small {\rm w}^2\) がつくことになります。
  • ここでは、事前に作成した「drawBrick」を利用する場合について解説しています。 このマクロを利用しない場合は、要するに 「描画するとき単位の付け方に注意してください!」 ということになります。変換段階では単位をつけず、xdraw()での 描画の最終段階で単位をつけてください。
  • 要するに、種々の定義は「単位抜き」で行い、 描画の最終段階で「単位をつける」必要があります。 その意味でも、transform値を定義するときは、 対応する座標の関係は単位付きではなく、 単位なしで指定した方がよいでしょう。あるいは、 変換を表すコマンドを書き並べる形で定義した方がよいかもしれません。 次々に、右側に書き並べていくだけです。
  • 以上のように、「単位の w, h などをつけるタイミング」には 注意する必要があります、この件については、 「みなも」さんにご指摘いただきました。
参考までに、同じ transform値を用いて、「XYTransformed t」とした場合と、 「ZXTransformed t」とした場合の図も上げておきます。
  • 「XYTransformed t」の場合
  • 「ZXTransformed t」の場合

▲戻る(トップメニューマップ)

■空間におけるアフィン変換
空間におけるアフィン変換はMetaPostには備わっていませんが、 MePoTeXではその変換を行うマクロが定義済みです。 平面の場合と同様に空間のアフィン変換は、 \(\small \mathbf{x}, \mathbf{x'}, \mathbf{p}\) を3次の 列ベクトルとして \(\small A\) を3次の正方行列とすると \[\small \mathbf{x'}=A\mathbf{x}+\mathbf{p}\] で表されます。 未知数は全部で12個あるので、 その値を決定するためにはcolor値変数(3次ベクトル)の対応関係が4個必要です。

空間におけるアフィン変換を行うには、次のような手順によります。

  1. 空間におけるアフィン変換を行う変数を、たとえば var とするとき、 その変数を「newTransform(var);」として宣言する。 これにより、擬似的に変換を実行するマクロ var.s と、 変換の成分を納める変数 var.p が確保される。
  2. 次に、具体的な対応関係を表す4個の式を、変換を実行するマクロ 「var.s」を利用して定める。たとえば、color値で表される点 a,b,c,d が、 それぞれ A,B,C,D に対応するときは、 「var.s a=A; var.s b=B; var.s c=C; var.s d=D;」とする。 これは、成分でいえば12個の式からなる連立1次方程式であるので、 MetaPostの機能で解くことができ、それにより変換の成分が決定する。
  3. 変換の成分が決まったら、その値を「realizeTransform(var);」として、 最初に定めた変数 var.p に納める。
  4. この変換で 点 p は「p Transformed var.p;」により変換される。

下記は、\(\small x\) 軸方向に 1/2倍、\(\small y\) 軸方向はそのまま、 \(\small z\) 軸方向には 2倍して、(2,1,0) 方向に平行移動した場合です。 具体的な対応関係は、分かりやすいように (2,0,0)が(3,1,0)に、(0,1,0)が(2,1,0)に、(0,0,1)が(2,1,2)に、 そして(0,0,0)が(2,1,0)に移るとして計算させました。 変換後が分かりやすいように、[F]では xdraw( ) のオプションに 「red」を指定しています。
  • \begin{MPpic} <1cm>(4|1,4|1) % [A]〜[D] \sendMP{  color cc[]; pair zz[]; % [E] 空間のアフィン変換  newTransform(var);  var.s (2,0,0)=(3,1,0);  var.s (0,1,0)=(2,2,0);  var.s (0,0,1)=(2,1,2);  var.s (0,0,0)=(2,1,0);  realizeTransform(var);  for n=0 upto 7:   cc[n]:=c[n]    Transformed var.p;  endfor; % [F] 移動後の描画  drawBrick(zz)(cc)(red); } \end{MPpic}

▲戻る(トップメニューマップ)

copyright