なんちゃってMathematicaを作る - その13 [なんちゃってMathematica]
この「なんちゃってMathematica」は最初からずっと概論のような話ばっかり続いて、さっそく飽きてきた。やっぱりもう少し具体的な話にならないとモチベーションが維持できない。かといってこれ用の評価エンジンの設計に入れるほど煮詰まっていない。
ということで、今日から違う側面の検討を進めることにする。昔やってて途中でくじけたプロットライブラリ。
今回「なんちゃってMathematica」のプロット機能のためにもういちどやり直すことにした。ほとんど忘れているので、レビューをやり直さないと同じところでまたくじけることになってしまう。
前回どこでくじけたか、というと3次元プロットでオブジェクトの前後関係の処理がめんどくさくて、そこで中断していた。今読んでみると一応問題は解決されているけど、組み合わせ爆発が起きてしまって効率のいい実装をしないと使用に耐えないと思ってくじけたようである。また当時のメモ(ブログにはあげてないLaTeXで書いた下書き)では、交差するオブジェクトをどう描画するか、という問題も棚上げになっていて考察を進めるところまでも行っていなかったらしい。
3次元プロットはそのように問題が残ったまんまになっているけど、とりあえず2次元プロットは一応明らかな問題点はなかったと思うので、とりあえず2次元プロットまでを実装しよう。それなら途中でくじけずにできるはずである。
ということで、もういちどプロットライブラリの設計を始めることにする。設計方針はまえやったのを思い出しながら、それに修正を加える、という方向でいきたい。
ではLaTeXに読み込むときに、ビットマップは何がダメかというと
わーどやえくせるぱわぽんとはちょっと違う、というLaTeXらしい美しさと同レベルの気の利いた図にするためにはこの編集は案外重要である。もちろんそれ以前にセンスの方が重要ではあるが。
PDFベースであることが最大の特徴なので、GPUによる高速化やOpenGLの機能は使えない。そのかわりしゃきっと美しい、という出力を狙う。
僕はそのかわり、Mac OS Xだけを前提にすることでコードの多くをCocoa App kitやQuartzに負わせることができるような方向をとることにする。誰の手も借りないかわりに、OSの手を借りるということである。
そうすれば、開発効率だけでなく、実行効率も同時に上げることができて(クロスプラットフォームにありがちなOSの違いを吸収するための中間コードがなくなる)、しかもApp Kit/Quartzの固有の機能(言ってしまえばpdfべったりの)を使うことでpdfらしい美しさを手に入れることができる。
ちなみに僕の考える悪しきクロスプラットフォームの典型は、最近のAdobeの製品。めちゃめちゃ遅くてメモリを大食いするうえに、MacらしくないLook&Feelで使う上で戸惑うことが多い。Adobeにとっては首尾一貫していいんだろうけど、ユーザがクロスプラットフォームに使う、ということはあまりない(会社でWindows、自宅でMacというのはあるけど、それぞれにIllustratorがある、なんて言う人はお金持ちだよな)ので、それぞれのプラットフォームで首尾一貫している方が普通のユーザにとっては望ましいはず。
これもまず2次元までを先に済ませることにする。たとえば色付け、破線処理、半透明化、影つけなんかは最終的にNSBezierPathに落としてQuartzの機能を使えば自動的にできる。また、Bezier Smoothingは以前やって実装したのがある。このへんは悩む必要はないので実装のときは楽になる。
気分的に2次元がきれいにできたら、3次元もやる気が出てちゃんと仕上げることができるようになるだろう。逆にいえば2次元がイマイチだったら気分的に、まあ、そこでフェイドアウトかな、ということもありえてしまうけど。
ふう、いっぱい書いた。
ということで、今日から違う側面の検討を進めることにする。昔やってて途中でくじけたプロットライブラリ。
1 はじめに、まず思い出す
以前、Mac専用のプロットライブラリを作ろうとしていた。Mathematicaの描画機能をまねて、それをフレームワークとしてCocoaとQuartzを利用して実装しようとしていたが、途中でくじけてほったらかしになっている。今回「なんちゃってMathematica」のプロット機能のためにもういちどやり直すことにした。ほとんど忘れているので、レビューをやり直さないと同じところでまたくじけることになってしまう。
前回どこでくじけたか、というと3次元プロットでオブジェクトの前後関係の処理がめんどくさくて、そこで中断していた。今読んでみると一応問題は解決されているけど、組み合わせ爆発が起きてしまって効率のいい実装をしないと使用に耐えないと思ってくじけたようである。また当時のメモ(ブログにはあげてないLaTeXで書いた下書き)では、交差するオブジェクトをどう描画するか、という問題も棚上げになっていて考察を進めるところまでも行っていなかったらしい。
3次元プロットはそのように問題が残ったまんまになっているけど、とりあえず2次元プロットは一応明らかな問題点はなかったと思うので、とりあえず2次元プロットまでを実装しよう。それなら途中でくじけずにできるはずである。
ということで、もういちどプロットライブラリの設計を始めることにする。設計方針はまえやったのを思い出しながら、それに修正を加える、という方向でいきたい。
2 外部仕様(機能)
まえにもまとめたけど、もう一度外部仕様をここに書き直しておく。2.1 特徴
最大のポイントは- PDFベースである
2.1.1 なぜPDFか?
ではなぜ、ビットマップでいけないのかというと、描画結果をLaTeXに読み込みたいからである。LaTeXでは図としてPDFがincludegraphicsマクロを使って読み込める。ではLaTeXに読み込むときに、ビットマップは何がダメかというと
- ビットマップでは解像度が決まってしまって高い表示品質が得られない
- ビットマップでは編集ができない
わーどやえくせるぱわぽんとはちょっと違う、というLaTeXらしい美しさと同レベルの気の利いた図にするためにはこの編集は案外重要である。もちろんそれ以前にセンスの方が重要ではあるが。
PDFベースであることが最大の特徴なので、GPUによる高速化やOpenGLの機能は使えない。そのかわりしゃきっと美しい、という出力を狙う。
2.1.2 PDF以外の特徴
それ以外の特徴として- Objective-CベースのCocoa frameworkとする
- Cocoaらしいシンプルさを持ったframeworkのインタフェイスにする
- Macの描画機能を、移植性を無視して使い切る(WindowsやほかのUnixは無視する)
- 高い描画効率を目指す
- 2D、3D(SurfaceGraphics)、density、Contourグラフを生成(Mathematicaが生成できるグラフを包含する)
- MacOSX独特の各種描画機能を利用した表現を可能にする
2.1.3 クロスプラットフォームについて
普通のプロットライブラリはクロスプラットフォーム(マルチプラットフォーム、異なるOSに同等の機能を持ったアプリをそれぞれ実装する、あるいはアプリの機能がOSによらない)を前提にしていて対応するOSの描画機能の最大公約数(APIの共通集合)的な機能以外はすべて内部で実装しているのがふつうである。したがって実装すべき機能の量は多くなってしまう。これは開発効率だけでなく、実行効率の低下も起しやすい。ふつうの人にとっては、一人で完結するにはヘビーな作業になりかねない。僕はそのかわり、Mac OS Xだけを前提にすることでコードの多くをCocoa App kitやQuartzに負わせることができるような方向をとることにする。誰の手も借りないかわりに、OSの手を借りるということである。
そうすれば、開発効率だけでなく、実行効率も同時に上げることができて(クロスプラットフォームにありがちなOSの違いを吸収するための中間コードがなくなる)、しかもApp Kit/Quartzの固有の機能(言ってしまえばpdfべったりの)を使うことでpdfらしい美しさを手に入れることができる。
ちなみに僕の考える悪しきクロスプラットフォームの典型は、最近のAdobeの製品。めちゃめちゃ遅くてメモリを大食いするうえに、MacらしくないLook&Feelで使う上で戸惑うことが多い。Adobeにとっては首尾一貫していいんだろうけど、ユーザがクロスプラットフォームに使う、ということはあまりない(会社でWindows、自宅でMacというのはあるけど、それぞれにIllustratorがある、なんて言う人はお金持ちだよな)ので、それぞれのプラットフォームで首尾一貫している方が普通のユーザにとっては望ましいはず。
2.2 ライブラリとしての機能
具体的なプロット機能として- 線、ポリゴン、文字列、bitmapの表示
- 2次元グラフ
- MathematicaのListPlotと同様の機能
- 棒グラフ、円グラフなど一般のグラフ描画
- 3次元グラフ
- MathematicaのListPlot3Dと同様の機能
- MathematicaのListDensityPlotと同様の機能
- MathematicaのListContourPlotと同様の機能
- 組み合わせグラフ
- MATLABのsurfcと同様の機能
- 任意の2次元データを3次元グラフの任意の面に貼付ける
- 線や面の色づけ、破線処理、矢印
- Bezier Smoothing
- 線や面の半透明化
- 2次元プリミティブへの影付け
- 文字列のグリフ選択
- 文字列の3次元面への貼付け
これもまず2次元までを先に済ませることにする。たとえば色付け、破線処理、半透明化、影つけなんかは最終的にNSBezierPathに落としてQuartzの機能を使えば自動的にできる。また、Bezier Smoothingは以前やって実装したのがある。このへんは悩む必要はないので実装のときは楽になる。
気分的に2次元がきれいにできたら、3次元もやる気が出てちゃんと仕上げることができるようになるだろう。逆にいえば2次元がイマイチだったら気分的に、まあ、そこでフェイドアウトかな、ということもありえてしまうけど。
ふう、いっぱい書いた。
2011-11-29 22:49
nice!(0)
コメント(8)
トラックバック(0)
こんには。
例えば私のところでは、2次元プロット(論文貼込み級グレードの作成)では、Mathematicaで下記のようなことを行い、ほぼ満足品質を得ています(Adobe Illustratorを用いて多少修正)。ただし、frameの横辺の大きさを固定させることができないので少し困りますね(目盛ラベルの数字の負符号や桁数が変わるのが原因)。
rangeWithMargin[range_, margin_: 0.02] :=
range + margin*{1, -1}*Subtract @@ range;
Table[{x, x*Sin[x]}, {x, -20, 20, 0.2}];
ListPlot[%, Joined -> True, Mesh -> Full,
PlotRange -> {rangeWithMargin[{0, 10}], Automatic}, Frame -> True,
FrameTicks -> Automatic, GridLines -> Automatic,
ImageSize -> {300, 200}, FrameLabel -> {None, "Y"}]
by zyx (2011-12-03 23:32)
コメントありがとうございます。
こういうプロットはMathematicaはほんとに美しいですよね。
Frameの大きさが変わってしまうのは、Frame内部のAspect ratioを合わせた上で外に目盛ラベルを配置したときにデフォルトのビューサイズの中におさまるようにするせいですよね。これは僕も、同じ横軸の図を並べたいときに困ります。特に縦軸のRangeの桁が違ってしまうとIllustratorでの小細工が困難になります。
でも、いいご指摘をいただきました。僕の「なんちゃってMathematica」のプロットフレームワークにはこれを調整するオプションを作ることにします。
ちゃんと最後まで仕上げられれば、ですが....
by decafish (2011-12-05 22:02)
こんにちは。
その後、私もMathematicaでframeの横辺の長さが固定されるような工夫に挑戦し、無理矢理に実現できました(相当に変ですが)。
ところで,Mathematicaが生成するプロット(の現状のpdfファイル)を調べてみました。でも、現状ではどれもleft alignedですが、FrameLabelと横軸labelはcentered aligned に、縦軸label(左辺)はright aligned となるのが嬉しいと思いました。
by zyx (2011-12-11 04:09)
僕も文字のアラインメントは自作プロットフレームワークで対応したかったことのひとつです。でもApp kit/Quartzの文字列をIllustractorに持っていったときにアラインメントが指定されるやり方がまだわかりません。
「横辺の長さが固定されるような工夫」はどのような方法でしょう?よろしければ教えて下さい。僕は縦軸の数値ラベルの桁数が同じになるように明示的にFrameTicksで与えるか、Illustratorへ持ってきて横軸の長さを測って同じになるように拡大縮小していました。
FrameTicksを指定するとAutomaticのときと同じにならないし、Illuatratorでは文字の縦横比が微妙にずれたりしてカッコわるくなってしまいます。
by decafish (2011-12-12 06:54)
こんにちは。
FrameTicksを指示する方法でやってみました。でもtickの長さの見栄えなどが気になり、Illuatratorで手直しは必要かもしれません。
frameTickFunc[stepMajor_, stepMinor_: None, opts___?OptionQ] :=
Module[{tick, step, width, siz, font, range2, tickWithLabel},
tick = {0.012, 0.006};
step = {stepMajor, stepMinor};
siz = FontSize /. {opts} /. FontSize -> 12;
font = FontFamily /. {opts} /. FontFamily -> "Courier";
width = LabelWidth /. {opts} /. LabelWidth -> 0;
range2[l_, u_, s_] :=
If[s === None, {},
Chop[Range[Ceiling[l/s]*s, Floor[u/s]*s, s]] ];
tickWithLabel[x_, i_] := Module[{str, blanks},
If[i != 1, str = "",
str = ToString[x];
blanks = Array[" " &, Max[0, width - StringLength[str]]];
str = StringJoin[Append[blanks, str]];
str = Style[str, FontFamily -> font, FontSize -> siz];
];
Return[{x, str, {tick[[i]], 0}}];
];
Flatten[
Table[Map[tickWithLabel[#, i] &, range2[#1, #2, step[[i]]] ], {i,2}], 1] &
];
Table[{x, x*Sin[x]}, {x, -20, 20, 0.2}];
ListPlot[%, Joined -> True, Mesh -> Full, PlotRange -> {rangeWithMargin[{0, 10}], Automatic}, Frame -> True, FrameTicks -> {frameTickFunc[2, 0.5], frameTickFunc[5, 1, LabelWidth -> 3], Automatic, Automatic}, GridLines -> Automatic, ImageSize -> {360, 240}, FrameLabel -> {"X", "Y"}]
by zyx (2011-12-15 00:22)
お手数をおかけしてすみません。
なるほど、すごいですね。全部がんじがらめに決めてしまおうということでしょうか。
ところで、zyxさんのMathematicaコードはMathematicaらしくてきれいだと僕は思います。勉強になります。
特に、Moduleの中でSetDelayedで関数を定義するのは初めて見ました。シンボル定義はグローバルなのにその関数定義はローカルなスコープになるのですね。おもしろいです。
by decafish (2011-12-16 08:15)
https://www.youtube.com/watch?v=Z9tAvKJ_Lh8
グラフは伊達に描くものに非ず
by nbgb (2019-08-08 10:22)
コメントありがとうございます。
グラフを描くときにはそうですね。
でもこういうコメントつまんないな。
上から目線で偉そうに言うだけで、
面白い話につながらないもんな。
by decafish (2019-08-08 12:22)