複素数を余裕でサポートしているMathematicaは、2Dグラフィックスをより簡単に処理できます。
座標としてリストを用いた星形の描画
例として星形を使います。
星形を描くには次のように組むとある程度すっきりするでしょう。
listPts = Table[{Cos[a Pi], Sin[a Pi]}, {a, 0, 4, 4/5}]
ListLinePlot[listPts, AspectRatio -> Automatic]
![mathematica_star_list.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F6117%2Fde2bea28-0dc9-1662-3ef3-2a53c52c5ba6.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=eed765f02798f3713b81e56749e37b6d)
平行移動
これを右に1、上に2だけ平行移動するには、以下のように座標 $(1, 2)$ を足せばいいでしょう。
listPts
に直接{1, 2}
を足すのも直感的に行けそうな気がするかもしれませんが、次元が合わないのが駄目なようなのでやむなくリストのそれぞれに適用させます。
listPts2 = (# + {1, 2}) & /@ listPts
ListLinePlot[listPts2, AspectRatio -> Automatic]
![mathematica_star_list_translate.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F6117%2F21157d33-9622-bbbb-c042-5607eec3cd4e.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=0c099107df11ea9ba34ea87544330883)
回転
ベクトル解析をやったことのある人はわかると思いますが、
ベクトルをアフィン変換(回転、拡大縮小など)するには行列を掛ける必要があります。
Mathematicaには回転行列などを求める関数があるので比較的楽に行えます。
listPts2 = (RotationMatrix[30 Degree].#) & /@ listPts
ListLinePlot[listPts2, AspectRatio -> Automatic]
![mathematica_star_list_rotate.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F6117%2F82fce0da-ec99-7750-1432-39288dfe4887.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=bcea7c19d90af2c2d16a00a609814750)
が、これもちょっと面倒だと(僕は)思います。
座標として複素平面を用いた星形の描画
複素平面上の複素数として座標値を扱うと、魔法のように扱いやすくなります。
compPts = (-1)^Range[0, 4, 4/5]
{Re[#], Im[#]} & /@ compPts
ListLinePlot[{Re[#], Im[#]} & /@ compPts, AspectRatio -> Automatic]
![mathematica_star_complex.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F6117%2Fe2425dd9-0721-0209-fbaa-b3040eac3c5d.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=227882625851fe7c66fabd2f13caf044)
{Re[#], Im[#]} & /@ compPts
が先ほどのlistPts
に相当します。
でも、まだ有り難みが感じられませんね。
では、以下の場合はどうでしょう。
平行移動
座標 $(1, 2)$ に相当する複素数 $1+2i$ を足すだけです。
compPts2 = compPts + (1 + 2 I)
ListLinePlot[{Re[#], Im[#]} & /@ compPts2, AspectRatio -> Automatic]
![mathematica_star_complex_translate.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F6117%2F988f2883-57fb-12be-3e75-41fed8d2dc35.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=6efac7dc2c1eb8189e97f3dc4b2c2948)
回転
30度($\frac{\pi}6$ ラジアン)回転は、複素数 $e^{\frac{\pi}6 i}$ を掛けるだけです。
compPts2 = E^(Pi/6 I) compPts
ListLinePlot[{Re[#], Im[#]} & /@ compPts2, AspectRatio -> Automatic]
![mathematica_star_complex_rotate.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F6117%2F31ab3b14-5e33-3752-adc7-7681517b8f6f.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=8f530820a6ea85399d8a7f6175c3fe83)
複素数でできること
アフィン変換のうち
- せん断(skew)
- アスペクト比が変わる伸縮
は難しいですが、
- 平行移動
- 回転
- 拡大縮小(→定数倍で可能)
は四則演算の組み合わせだけで行えます。
Complex
複素数は
In[1] = FullForm[1 + 2 I]
Out[1] = Complex[1, 2]
となるように、頭部がComplexとなった要素で、
実部(Re
)と虚部(Im
)を取り出すには先ほどのように
{Re[#], Im[#]} &
という関数を通せばリストとして返ってきます。
Mathematicaにそこそこ詳しい人なら、
List @@ (1 + 2I)
とやって頭部Complex
をList
に置き換えればいいじゃん、と
思いつくかもしれませんが、何故かその手は禁じられています。
(1 + 2I)[[{1, 2}]]
これも駄目です。
逆に、
Complex @@ {1, 2}
とやってリストを複素数にすることは可能です。
いっそのこと
reim[c_?NumberQ] := {Re[c], Im[c]}
reim[li_List] := {Re[#], Im[#]}& /@ li
とか関数作っちゃってもいいかと、記事を書きながら思いましたが、
もはや{Re[#],Im[#]}&
の記述に慣れすぎてるので有用かどうかわかりません。
追記2018-12-15: バージョン10.1から ReIm
関数が導入されています。まんま!
ReIm—Wolfram言語ドキュメント
本当、役に立ちます。
来週執筆予定のコードゴルフ連載でも頻発することになると思いますが、
座標をリストとせず複素数とすることにより、一数値扱いになってある意味カプセル化的な役割を果たしているので、たとえばMap
(/@
演算)やFlatten
を多用するときに、リストよりかなり都合がいいのです。
(場合によってはComplex
やList
の代わりにPoint
を使ってもいいかもしれませんが、あくまでもグラフィックスプリミティブなのでちょっと違う気がします。)
騙されたと思ってやってみてください。