ujimushi(@旧sradjp(15364))の日記

旧スラドの日記の引越先です

((top|bottom|left|right)_|)margin(Plots.jl(GR))

ほぼ初めてのfontやテキスト以外のアトリビュートの説明になります。 この説明をするのは,title_locationでもplot_titlelocation と同じようなことができることが分かったため,マージン関係を 説明しておいた方がいいと判断したためです。

早速説明していきます。

概念図

大作です(?)。 前回のtitle_locationと似たような図です。なお,前回確認し忘れていましたが,plot_title=""(空文字)の時は plot_titlevspan0相当となります。たとえ値を個別に設定したとしてもです。

一応ソースリストも。

using TikzPictures

function smaller(rect::@NamedTuple{x₀::Float64, y₀::Float64,
                                   x₁::Float64, y₁::Float64},
                 w::Number; type=:all)
    if type == :top
        (x₀=rect.x₀, y₀=rect.y₀, x₁=rect.x₁, y₁=rect.y₁ - w)
    elseif type == :bottom
        (x₀=rect.x₀, y₀=rect.y₀ + w, x₁=rect.x₁, y₁=rect.y₁)
    elseif type == :left
        (x₀=rect.x₀ + w, y₀=rect.y₀, x₁=rect.x₁, y₁=rect.y₁)
    elseif type == :right
        (x₀=rect.x₀, y₀=rect.y₀, x₁=rect.x₁ - w, y₁=rect.y₁)
    else
        (x₀=rect.x₀ + w, y₀=rect.y₀ + w, x₁=rect.x₁ - w, y₁=rect.y₁ - w)
    end
end

function tikzdrawrect(rect::@NamedTuple{x₀::Float64, y₀::Float64,
                                        x₁::Float64, y₁::Float64}; option="")
    "\\draw[draw, $option] ($(rect.x₀), $(rect.y₀)) rectangle ($(rect.x₁), $(rect.y₁));"
end

r_whole = (x₀=0.0, y₀=0.0, x₁=10.0, y₁=6.0)
r_sps = smaller(r_whole, r_whole.y₁ * 0.3; type=:top)
r_sp = [smaller(r_sps, r_sps.x₁ * 0.5; type=rl) for rl in [:right, :left]]
r_spm = smaller.(r_sp, 0.5)

r_gd = [(x₀=r.x₀ + 0.45, y₀=r.y₀+0.45, x₁=r.x₁, y₁= r.y₁ - 0.5) for r in r_spm]
grid_latex = string(["\\draw[draw, gray] ($(r.x₀), $(r.y₀)) grid[step=2mm] ($(r.x₁), $(r.y₁));\n"
                     for r in r_gd]...)

coordinate_latex = string(
    ["\\node[red] at ($(r[k[1] * 2 + 1]), $(r[k[2] * 2 + 2])) {$k};\n"
     for r in r_gd for k in [(0, 0), (0, 1), (1, 0), (1, 1)]]...)
position_latex = string(
    ["\\node[red, anchor=north$⚓] at ($(r.x₀*k[1]+r.x₁*k[2]), $(r.y₁)) {$pos};\n"
     for r in r_spm for (⚓, pos, k) in zip([" west", "", " east"],
                                            [":left", ":center", ":right"],
                                            [(1, 0), (0.5, 0.5), (0, 1)])]...)

x₁ = r_whole.x₁ * 0.2
x₂ = r_sp[2].x₀ + x₁
y₁ = (r_spm[1].y₀ + r_spm[1].y₁) / 2 + 0.8
y₂ = y₁ - 0.8 * 2

p_c = [(x=(r.x₀ + r.x₁) / 2,  y=(r.y₀ + r.y₁) / 2) for r in r_sp]
arrows_latex = begin
    条件s = [(p₁ = (x₁, r_sp[1].y₁), p₂ = (x₁, r_spm[1].y₁), ⚓="west", t="top", r=0),
             (p₁ = (x₂, r_sp[2].y₁), p₂ = (x₂, r_spm[2].y₁), ⚓="west", t="top", r=0),
             (p₁ = (x₁, r_sp[1].y₀), p₂ = (x₁, r_spm[1].y₀), ⚓="west", t="bottom", r=0),
             (p₁ = (x₂, r_sp[2].y₀), p₂ = (x₂, r_spm[2].y₀), ⚓="west", t="bottom", r=0),
             (p₁ = (r_sp[1].x₀, y₁), p₂ = (r_spm[1].x₀, y₁), ⚓="east", t="left", r=90),
             (p₁ = (r_sp[2].x₀, y₁), p₂ = (r_spm[2].x₀, y₁), ⚓="east", t="left", r=90),
             (p₁ = (r_sp[1].x₁, y₂), p₂ = (r_spm[1].x₁, y₂), ⚓="west", t="right", r=90),
             (p₁ = (r_sp[2].x₁, y₂), p₂ = (r_spm[2].x₁, y₂), ⚓="west", t="right", r=90)]
    string(["\\draw[<->, thick, blue] $(条件.p₁) -- node[anchor=$(条件.⚓), rotate=$(条件.r)] {$(条件.t)\\_margin} $(条件.p₂);\n"
            for 条件 in 条件s]...)
end
tp = TikzPicture(
"""
$(tikzdrawrect(r_whole; option="black, fill=black!10, thick"))
$(tikzdrawrect(r_sp[1]; option="blue, thick"))
$(tikzdrawrect(r_sp[2]; option="blue, thick"))
$(tikzdrawrect(r_spm[1]; option="blue, dashed, fill=blue!30, thick"))
$(tikzdrawrect(r_spm[2]; option="blue, dashed, fill=blue!30, thick"))
$arrows_latex
$grid_latex
$coordinate_latex
$position_latex
\\node[blue] at ($(p_c[1].x), $(p_c[1].y)) {\\textbf{Subplot(No.1)}};
\\node[blue] at ($(p_c[2].x), $(p_c[2].y)) {\\textbf{Subplot(No.2)}};
\\draw[<->, very thick] ($(x₁), $(r_sps.y₁)) -- ($(x₁), $(r_whole.y₁))
node[anchor=south, text width=4cm, align=center] {\\textbf{plot\\_titlevspan \\\\ (heightとの比を指定)}};
\\draw[<->, thick] (-0.5, $(r_whole.y₁))
 -- node[anchor=south, rotate=90] {height} (-0.5, $(r_whole.y₀));
node[anchor=south] {\\textbf{plot\\_titlevspan}};
\\begin{scope}[on background layer]
\\fill[fill=white] (current bounding box.south west) rectangle (current bounding box.north east);
\\end{scope}
"""
, preamble="""
  \\usepackage{luatexja}
  \\usetikzlibrary{arrows.meta}
  \\usetikzlibrary{backgrounds}
  """
)

save(SVG("margin"), tp)
run(`inkscape -w 768 -o margin.png margin.svg`)

別名

それぞれ次の通りです。

  • margin, margins
  • top_margin, top_margins, topmargin, topmargins
  • bottom_magin, bottom_margins, bottommargin, botoommargins
  • left_margin, left_margins, leftmargin, leftmargins
  • right_margin, right_margins, rightmargin, rightmargins

marginに設定する値

バージョン1.XX,2共通

共通で使えるのはタプルのみになりましたタプルとPlots.単位です。

1cmを設定する方法は次の通りです。

  • (1.0, :cm)
  • (10.0, :mm)
  • 1Plots.cm
  • 10Plots.mm

二つ目の単位で設定可能なのは

  • cm, mm, inch, pt, px

です。ただし,Symbol型にして設定する必要があります。次のような感じで利用します。

using Plots
import GR  # バージョン2では必須
gr()       # バージョン2では必須

plot(sin; bottom_margin=(1.0, :cm))

バージョン1.XXのみ。

1cmを設定する方法を思いつく限り列挙してみます。

  • Plots.Measures.Length(:cm, 1.0)
  • Plots.PlotMeasures.Length(:cm, 1.0)
  • Plots.Length(:cm, 1.0)
  • 1.0Plots.Measures.cm
  • 1.0Plots.PlotMeasures.cm
  • 1.0Plots.cm
  • Plots.Measures.Length(:mm, 10.0)
  • Plots.PlotMeasures.Length(:mm, 10.0)
  • Plots.Length(:mm, 10.0)
  • 10.0Plots.Measures.mm
  • 10.0Plots.PlotMeasures.mm
  • 10.0Plots.cm

どれも元々の実体はMeasures.jlです。 こちらで確認して指定可能なのは

  • cm, mm, inch, pt, px

です。

実際に使う時には

using Plots

const mm = Plots.mm

plot(sin; bottom_margin=10mm)

のような感じで使うのが便利かもしれません。 using PlotMeasuresとする方法もありますが,意図しない名前が重なってしまう可能性があるので importで使うものだけモジュール名を省略する方がいいでしょう。

バージョン2のみ

1cmを設定する方法を思いつく限り列挙してみます。

  • PlotsBase.Commons.Length(:mm, 10)
  • PlotsBase.Commons.Measures.Length(:mm, 10)
  • 10.0PlotsBase.Commons.Measures.mm
  • 10.0PlotsBase.Commons.mm
  • PlotsBase.Commons.Length(:cm, 1)
  • PlotsBase.Commons.Measures.Length(:cm, 1)
  • 1.0PlotsBase.Commons.Measures.cm
  • 1.0PlotsBase.Commons.cm
  • 10Plots.mm
  • 1Plots.cm

バージョン2のことを考えると,タプルを使う方法が無難のような気がします。 タプルを使う方法は,わりとネットでは紹介されていないような気もします。

追記 : バージョン2でも Plots.mm 等が使えそうなことを確認しました。

使用例

marginleft_marginright_margintop_marginbottom_marginを混ぜると,個別に設定したもの優先になります。 次のような感じ。

using TikzPictures
using Plots
import GR
gr()

plot(sin; margin=(5, :mm), left_margin=(10, :mm), bottom_margin=(15, :mm))
savefig("margin_left_bottom.png")

tp = TikzPicture(
"""
\\node[anchor=south west, inner sep=0mm] (G) {\\includegraphics[width=15.24cm]{margin_left_bottom.png}};
\\draw[dotted, blue, very thick] ([yshift=-5mm, xshift=10mm]G.north west)
 rectangle ([xshift=-5mm, yshift=15mm]G.south east);
\\draw[<->, thick] (G.north) -- node[anchor=west] {margin=5mm} ([yshift=-5mm]G.north);
\\draw[<->, thick] (G.west) -- node[anchor=west, rotate=90] {left\\_margin=10mm} ([xshift=10mm]G.west);
\\draw[<->, thick] (G.east) -- node[anchor=west, rotate=90] {margin=5mm} ([xshift=-5mm]G.east);
\\draw[<->, thick] (G.south) -- node[anchor=west] {bottom\\_margin=15mm} ([yshift=15mm]G.south);
"""
, preamble="""
  \\usepackage{luatexja}
  \\usepackage{graphicx}
  """
)

save(SVG("margin_lr.svg"), tp)
run(`inkscape -w 1024 -o plot_margin_lr.png margin_lr.svg`)

個別に設定していない,上側と右側のマージンがmarginで設定している5mm, 個別に設定している左と下のマージンは設定した値になっています。

複数のsubplotの時のmarginの注意点

複数のsubplotを指定した時は少し挙動が違います。 例を示します。

using Plots
import GR
gr()

plot(
    plot(sin; label="A", top_margin=(1, :cm), left_margin=(1, :mm),
         right_margin=(1, :mm), bottom_margin=(1, :mm)),
    plot(sin; label="B", top_margin=(1, :mm), left_margin=(1, :cm),
         right_margin=(1, :mm), bottom_margin=(1, :mm)),
    plot(sin; label="C", top_margin=(1, :mm), left_margin=(1, :mm),
         right_margin=(1, :cm), bottom_margin=(1, :mm)),
    plot(sin; label="D", top_margin=(1, :mm), left_margin=(1, :mm),
         right_margin=(1, :mm), bottom_margin=(1, :cm))
)

savefig(joinpath(path_env, "subplot_margins.png"))

subplotはそれぞれ青の点線が指定したマージンですが,実際には赤の点線のマージン 表示されています。

これは,一行目についてで言うと AとBのtop_marginの大きい方が採用されていて, AとBのbottom_marginは両方とも1mmなので1mmのまま。

一列目のAとCのleft_marginは両方とも1mmなので1mmのまま。rihgt_marginは Cの方が1cmと大きいので両方ともこの値が採用されている...

という挙動になっているようです。

(top|bottom)_marginはそれぞれの行の最大値が,(left|right)_marginはそれぞれの列の最大値 で表示されているようです。

結構めんどくさい仕様で動いているようですね。

名前 subplotのポジション
A (1,1)
B (1,2)
C (2,1)
D (2,2)
名前 top_margin left_margin right_margin bottom_margin
A 10mm 1mm 1mm 1mm
B 1mm 10mm 1mm 1mm
C 1mm 1mm 10mm 1mm
D 1mm 1mm 1mm 10mm
位置 top_margin left_margin right_margin bottom_margin
1行目 10mm -- -- 1mm
2行目 1mm -- -- 10mm
1列目 -- 1mm 10mm --
2列目 -- 10mm 1mm --

その他

なお,画面表示に関しては,画面表示とこれらの設定は異なるように見えることはしょっちゅうです。 というのも設定しているサイズとウィンドウのサイズがの整合性が今一つと思うことがよくあり, 私の環境では設定しているサイズに対してウィンドウの横幅が広めに表示されます。

Plots.jl各APIを介して自分で制御することもできないので,実質画像等への出力時の制御と思うようにしています。