知りたいこと
using Plots
plt = plot([sin, cos])
savefig(plt, "sincos.png")
上の例において、プロットを画像化する際の処理を詳しく調べたい。
plt
の内部構造も気になるところだが、その点については機会を改めたい。
output.jl
savefig
savefig(plt, "sincos.png")
ファイル名の拡張子に応じて、対応する関数 (この場合は png
) に処理を受け渡す。
png
png(plt, "sincos.png")
PNG の MIME を持たせて Base.show
に処理を受け渡す。
Base.show
https://github.com/JuliaPlots/Plots.jl/blob/master/src/output.jl#L210
(様々な MIME に対してメタプログラミングで定義されている)
io = open("sincos.png", "w")
show(io, MIME("image/png"), plt)
本来は、名前の通りに表示を目的とした関数である。
https://docs.julialang.org/en/v1/base/io-network/#Base.show-Tuple{IO,Any,Any}
ファイルへの書き込みは IO にデータを流し込むという処理であり、表示と共通化できるので、 Base.show
でまとめて請け負っているのだろう。
prepare_output
(ビューポイントの設定?)の後、内部関数 _show
に処理を受け渡す。
--
【補足】
例えば、
plt = plot([sin, cos])
などで直接プロットが表示される場合も、暗黙的に呼ばれる display(plt)
からこの show
に処理が移譲される。
backends/gr.jl
内部関数 _show
について説明していく。
https://github.com/JuliaPlots/Plots.jl/blob/master/src/backends/gr.jl#L1968
バックエンドに応じた分岐
この _show
で、バックエンドごとに処理が分岐する。バックエンドの情報を plt
変数の型パラメータで保持し、多重ディスパッチしている。
typeof(plt)
# Result: Plots.Plot{Plots.GRBackend}
_show(io, MIME("image/png"), plt)
# backends/gr.jl
function _show(io::IO, ::MIME{Symbol("image/png")}, plt::Plot{GRBackend})
# 省略
end
本記事ではバックエンドを GR として進めていくが、他のバックエンドでもそれぞれ _show
が定義されている。プロット時には、バックエンドに対応した _show
メソッドが呼ばれることになる。各バックエンドでの _show
の定義は backends ディレクトリに含まれている。
_show の定義を得る
そのままでは読みにくいので、 quote
で囲んで補間しよう。
https://docs.julialang.org/en/v1/manual/metaprogramming/#Interpolation-1
mime, fmt = "image/png" => "png"
quote
function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
GR.emergencyclosegks()
filepath = tempname() * "." * $fmt
env = get(ENV, "GKSwstype", "0")
ENV["GKSwstype"] = $fmt
ENV["GKS_FILEPATH"] = filepath
gr_display(plt, $fmt)
GR.emergencyclosegks()
write(io, read(filepath, String))
rm(filepath)
if env != "0"
ENV["GKSwstype"] = env
else
pop!(ENV,"GKSwstype")
end
end
end
出力の quote
を外して、適当にコメントを追加した。
function _show(io::IO, ::MIME{Symbol("image/png")}, plt::Plot{GRBackend})
GR.emergencyclosegks()
filepath = tempname() * "." * "png" # データのやり取りに使用する一時ファイル名
env = get(ENV, "GKSwstype", "0") # 設定の退避
ENV["GKSwstype"] = "png" # フォーマットを環境変数で設定
ENV["GKS_FILEPATH"] = filepath # 出力ファイル名も同様
gr_display(plt, "png") # プロットを画像化して一時ファイルに保存
GR.emergencyclosegks()
write(io, read(filepath, String)) # 画像を読み出して IO に出力
rm(filepath) # 一時ファイルを削除
if env != "0"
ENV["GKSwstype"] = env # 設定を元に戻す
else
pop!(ENV, "GKSwstype")
end
end
GR の環境変数を用いた設定一覧
https://gr-framework.org/environment_variables.html