今日、私はパッケージを扱うために再びあなたの助けを必要としていますforest
(これは本当に強力なパッケージですが、かなり複雑でもあります)。基本的に、私はすでに数時間を費やして、次のようなツリー形式の最後通牒ゲームを描こうとしています。
これまでのところ、何とか動作する(ただし、うまく動作しない)このコードを作成することに成功しました。
\documentclass{report}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{mathtools}
\usepackage{forest}
\usepackage[labelfont=bf,skip=0pt,labelsep=period]{caption}
\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.6}
\usetikzlibrary{matrix,calc,positioning}
\pgfplotsset{soldot/.style={color=black,only marks,mark=*}}
\pgfplotsset{/pgfplots/xlabel near ticks/.style={/pgfplots/every axis x label/.style={at={(ticklabel cs:0.5)},anchor=near ticklabel}},/pgfplots/ylabel near ticks/.style={/pgfplots/every axis y label/.style={at={(ticklabel cs:0.5)},rotate=90,anchor=near ticklabel}}}
\begin{document}
\begin{figure}
\begin{forest}
for tree={l sep=4em, s sep=8em, anchor=center}
[$P_1$, circle, draw,
[,name=0, edge label={node[midway,left,outer sep=1.5mm,]{$x=0$}}]
[$P_2$, l*=2, before computing xy={s=(s("!p")+s("!n"))/2}, circle, draw, edge label={node[midway,right,]{$x$}}
[{$c-x,x$}, edge label={node[midway,left,outer sep=1.5mm,]{$Y$}}]
[{$0,0$}, edge label={node[midway,right,outer sep=1.5mm,]{$N$}}]]
[,name=1, edge label={node[midway,right,outer sep=1.5mm,]{$x=c$}}]]
\draw (0) to[bend right=45] (1);
\end{forest}
\end{figure}
\end{document}
得られた出力は次のようになります。
理想的には、修正したい点がいくつかあります。まず、(a) 曲線が木の枝に触れて、醜い空白を避けたいです。次に、(b) 曲線とノード P2 が重ならないようにしたいです。3 番目に、(c) 2 つのノードの 2 つの枝の角度が同じになるようにしたいです。つまり、2 番目のノードの角度は 1 番目のノードの角度よりも広く、同じにしたいです。4 番目に、(d) 可能であれば、2 番目のノードの枝を 1 番目のノードの枝よりも短くしたいです。すべての質問は同様に関連しています。4 つの問題のいずれかについて、ご助力いただければ幸いです。
PS: 理想的には、現在のコードをできるだけ維持したいと思います (言い換えると、これは多かれ少なかれ簡単に理解できるコードなので、必要な変更を得るためにコードに最小限の変更を適用したいと思います)。
答え1
ここに、述べられた要件をすべて満たしていると思われる解決策があります。ターゲット イメージから、円弧がノードの最上部のポイントより短くならないようにしたいと想定しましたP_2
。それが必要ない場合は、私のコードに含まれる計算の一部を気にする必要はありません。
\documentclass[tikz, border=10pt, multi]{standalone}
円弧を正しく描画するために、TikZ ライブラリthrough
をロードします。calc
\usetikzlibrary{through,calc}
\usepackage{forest}
ここで、いくつかのスタイルを設定します。これらのスタイルの一部は、既存のコードを簡素化します。使用しない場合は省略できます。
\forestset{%
auto edge label
の書式設定のコードを自動化しますedge label
。ノードを作成し、内容を数式モードにし、ラベルを端の左に配置するか右に配置するかを決定します。つまり、
edge label=x^2, auto edge label
正しいことをするでしょう。
auto edge label/.style={%
before typesetting nodes={%
ルートノードの場合は何もしないでください。
if level=0{}{
ノードが親の子の後半にある場合、または中間の子である場合...
if={n()>(n_children("!u")/2)}{
ノードが中間の子である場合...
if={n()==((n_children("!u")+1)/2)}{
edge label/.wrap value={
node[midway, right] {$##1$}
},
}{
ノードが親の子の後半にある場合...
edge label/.wrap value={
node[midway, outer sep=1.5mm, right] {$##1$}
},
},
}{
ノードが親の子の最初の半分にある場合...
edge label/.wrap value={
node[midway, outer sep=1.5mm, left] {$##1$}
},
}
},
},
},
これは、より見栄えの良い空のノードのスタイルです。これは、現在のマニュアルの 65 ページからのものです。これはライブラリの一部ですlinguistics
。したがって、そのライブラリを使用する場合は、この定義を省略して、そのままのスタイルを適用できます。
マニュアルのこの部分の説明では、存在しないオプションについて誤って言及しているように思いますが、この点については確信がありません。
nice empty nodes/.style={% page 65 of the manual - this is from the linguistics library
for tree={
calign=fixed edge angles
},
delay={
where content={}{
shape=coordinate,
for parent={
for children={anchor=north}
}
}{}
}
},
円弧を描画するためのスタイルは次のとおりです。円弧を描画するノードの親にオプションとして渡します。
2 番目の子を除き、ツリー自体の一部としてエッジは描画されません。子が 3 つ以上ある場合は、もう少し複雑にする必要があります。このコードでは、子が 3 つあると想定しています。
代わりに、 を使用する場合、中間ノードの位置とノードに対するデフォルトの角度に基づいて適切なポイントを計算することによって、後で円弧が描画されますcalign=fixed edge angles
。この時点で、最初の子と 3 番目の子へのエッジが描画されます。
[これは、さまざまな可能性をチェックするという点で、実際にはもう少し洗練されているはずですが、MWE のような関連するケースでは機能するはずです。]
arc below/.style={
tikz+={%
\clip (.center) coordinate (o) -- (!1.north) coordinate (a) |- (!2.north) coordinate (b) -| (!3.north) coordinate (c) -- cycle;
\node [draw, circle through={(b)}] at (o) {};
\draw [\forestoption{edge}] () -- ($(o)!1!-35:(b)$) ($(o)!1!35:(b)$) -- ();
},
for children={
if n=2{}{no edge},
}
}
}
利便性を考慮したシンプルな TikZ スタイル。
\tikzset{%
my circle/.style={draw, circle}
}
次に、これらすべてを次のようにツリーに適用します。
\begin{document}
\begin{forest}
for tree={
MWEより。
l sep=4em,
s sep=8em,
新しいスタイルのうち 2 つをツリー全体に適用します。
auto edge label,
nice empty nodes,
ドル記号を節約するには、すべてのノードを数式モードにします。
math content,
}
arc below
ルートのスタイルを指定します。
[P_1, my circle, arc below
[, edge label={x=0}]
[P_2, my circle, edge label=x
[{c-x,x}, edge label=Y]
[{0,0}, edge label=N]
]
[, edge label={x=c}]
]
\end{forest}
\end{document}
完全なコード:
\documentclass[tikz, border=10pt, multi]{standalone}
\usetikzlibrary{through,calc}
\usepackage{forest}
\forestset{%
auto edge label/.style={%
before typesetting nodes={%
if level=0{}{
if={n()>(n_children("!u")/2)}{
if={n()==((n_children("!u")+1)/2)}{
edge label/.wrap value={
node[midway, right] {$##1$}
},
}{
edge label/.wrap value={
node[midway, outer sep=1.5mm, right] {$##1$}
},
},
}{
edge label/.wrap value={
node[midway, outer sep=1.5mm, left] {$##1$}
},
}
},
},
},
nice empty nodes/.style={% page 65 of the manual - this is from the linguistics library
for tree={
calign=fixed edge angles
},
delay={
where content={}{
shape=coordinate,
for parent={
for children={anchor=north}
}
}{}
}
},
arc below/.style={
tikz+={%
\clip (.center) coordinate (o) -- (!1.north) coordinate (a) |- (!2.north) coordinate (b) -| (!3.north) coordinate (c) -- cycle;
\node [draw, circle through={(b)}] at (o) {};
\draw [\forestoption{edge}] () -- ($(o)!1!-35:(b)$) ($(o)!1!35:(b)$) -- ();
},
for children={
if n=2{}{no edge},
}
}
}
\tikzset{%
my circle/.style={draw, circle}
}
\begin{document}
\begin{forest}
for tree={
l sep=4em,
s sep=8em,
auto edge label,
nice empty nodes,
math content,
}
[P_1, my circle, arc below
[, edge label={x=0}]
[P_2, my circle, edge label=x
[{c-x,x}, edge label=Y]
[{0,0}, edge label=N]
]
[, edge label={x=c}]
]
\end{forest}
\end{document}
編集
ツリーの最初の 2 つのレベル間の分離をさらに広げたい場合、最も簡単な方法は、l sep
ルート ノードの の値を増やすことです。以下は意図的に誇張した例です。
\begin{forest}
for tree={
l sep=4em,
s sep=8em,
auto edge label,
nice empty nodes,
math content,
}
[P_1, my circle, arc below, l sep*=6
[, edge label={x=0}]
[P_2, my circle, edge label=x
[{c-x,x}, edge label=Y]
[{0,0}, edge label=N]
]
[, edge label={x=c}]
]
\end{forest}
ここで、ルートとその子の間の最小距離は、 によって、通常のレベル間の最小距離の 6 倍に設定されていますl sep*=6
。絶対値を追加する場合は、 と指定できますl sep+=<dimension>
。または、デフォルトをオーバーライドする場合は、l sep=<dimension>
によって最小距離が正確に指定されます。
重要なのはl sep
、最小距離。したがって、l sep
1つのレベルでは非常に小さく設定され、別のレベルでは少し大きく設定されている場合、他の要因によって、どちらの場合も同じ距離になる可能性があります。森指定された最小値のいずれかよりも大きな距離でノードを配置する必要があります。
実際のターゲット ツリーは、質問で示したものとはまったく異なることに注意してください。実際、上記のコードの中で最も難しい部分は、最終的なツリーにはまったく必要ありません。
参照用に、このツリーの自動化バージョンを示します。このバージョンでは、TikZ ライブラリは にのみ必要なので使用しませんarc below
。はarc through
、北のアンカーを通過せずに西と東のアンカーに接続する新しいスタイルです。my arc
円弧のスタイルを決定します。これは、スタイルを決定するためにを使用してツリー内で設定できますmy arcs={<key list>}
。デフォルトでは、これは空で、円弧は現在のオプションのスタイルで描画されますedge
。でスタイルを指定すると、スタイルmy arcs
を補足または上書きできますedge
。たとえば、densely dashed
エッジがしっかりと描画されていても、円弧は均一になる可能性があります。
\forestset{%
arc through/.style={
tikz+={%
\path [\forestoption{edge}, my arc] (!1) [out=-35, in=180] to (!2.west) (!2.east) [out=0, in=-145] to (!3);
}
},
my arcs/.code={%
\tikzset{%
my arc/.style={#1},
}
},
}
\tikzset{%
my arc/.style={},
}
\documentclass[tikz, border=10pt, multi]{standalone}
\usepackage{forest}
\forestset{%
auto edge label/.style={%
before typesetting nodes={%
if level=0{}{
if={n()>(n_children("!u")/2)}{
if={n()==((n_children("!u")+1)/2)}{
edge label/.wrap value={
node[midway, right] {$##1$}
},
}{
edge label/.wrap value={
node[midway, outer sep=1.5mm, right] {$##1$}
},
},
}{
edge label/.wrap value={
node[midway, outer sep=1.5mm, left] {$##1$}
},
}
},
},
},
nice empty nodes/.style={% page 65 of the manual - this is from the linguistics library
for tree={
calign=fixed edge angles
},
delay={
where content={}{
shape=coordinate,
for parent={
for children={anchor=north}
}
}{}
}
},
arc through/.style={
tikz+={%
\path [\forestoption{edge}, my arc] (!1) [out=-35, in=180] to (!2.west) (!2.east) [out=0, in=-145] to (!3);
}
},
my arcs/.code={%
\tikzset{%
my arc/.style={#1},
}
},
}
\tikzset{%
my circle/.style={draw, circle},
my arc/.style={},
}
\begin{document}
\begin{forest}
for tree={
l sep=4em,
s sep=8em,
auto edge label,
nice empty nodes,
math content,
my arcs={densely dashed},
}
[P_1, my circle, arc through
[, edge label={x=0}]
[P_2, my circle, edge label=x
[{c-x,x}, edge label=Y]
[{0,0}, edge label=N]
]
[, edge label={x=c}]
]
\end{forest}
\end{document}
編集は脇に置いて
このバージョンはクレマン546 文字以上ありますが、それでも 644 文字です。また、Kile は TikZ のみのコードを 563 文字にしているので、おそらく私の統計では文字数が異なっています。
個人的には、これは利点ではないと思いますが、それが現実です。
あまり透明ではないので、実際にこれを使用することはお勧めしません。
ただし、円弧の描画方法は以前のコードよりもはるかにきれいです。おそらく、ライブラリarc below
を使用するのではなく、この方法をベースにするでしょうthrough
。
文字数の節約は、主に自動化を排除することで実現されています。エッジ ラベルは、兄弟に対する子の位置に応じて自動的に配置されなくなりました。そのため、ノードを追加する場合は、left
s が s になるかright
、またはその逆になるかを確認する必要があります。さらに、円にはスタイルが使用されないため、コードの柔軟性と保守性が低下します。最後に、ノードの内容にドル記号が使用されるのは、各ノードに必要なドル記号のペアを割り当てるために必要な文字数よりも多くの文字が含まれているmath content
ためです。math content,
皮肉なことに、弧を描くのに実際に使われているのは森機能がより重視され、TikZ 機能はそれほど重視されません。( は、ライブラリy()
に依存せずに、アークに必要な情報を取得するために pgfmath ラッパーと共に使用されますthrough
。)
\documentclass{standalone}
\usepackage{forest}
\usetikzlibrary{calc}
\begin{document}
\begin{forest}
ey/.style={shape=coordinate,no edge},
elr/.style 2 args={edge label={node[midway,outer sep=1.5mm,#1]{$#2$}}},
el/.style={elr={left}{#1}},
er/.style={elr={right}{#1}},
for tree={l sep=4em,s sep=8em,calign=fixed edge angles}
[$P_1$,draw,circle
[,el={x=0},ey]
[$P_2$,draw,circle,er=x,anchor=north,before drawing tree={TeX/.wrap pgfmath arg={\gdef\rs{#1}}{y("!u")-y()}},tikz={\draw(!u)--($(!u)!1!-35:(.north)$)arc(235:305:\rs pt)--(!u);}
[${c-x,x}$,el=Y]
[${0,0}$,er=N]]
[,er={x=c},ey]]
\end{forest}
\end{document}
答え2
それに固執したい気持ちはわかりますforest
が、回答を待つ間、貧乏人の方法で計画を描く方法を理解しようとすることができます。それほど難しくはありません。
P1
どこかに描いてみよう
\node[circle,draw] (P1) {$P_1$};
P2
以下に一定の距離で描画してみましょうP1
\node[circle, draw, on grid, below = 2cm of P1, anchor=north] (P2) {$P_2$};
とするon grid
と、 P1 の中心と P2 の北の間の距離anchor=north
が強制されます2cm
。完全な円弧を描くにはこれが必要になります。
P1
これで、との間に線を引くことができますP2
\draw (P1)-- node[right]{$x$} (P2);
次に、左の子と右の子にどの角度が必要かを決定します。腕の長さは中心2cm
から始まりますP1
。右の子の終わりには、最初の角度、最後の角度、半径がわかっているので、円弧を描きます。
\draw (P1) -- node[right] {$x=c$} ++ (-60:2cm) arc (-60:-120:2cm);
\draw (P1) -- node[left] {$x=0$} ++ (240:2cm);
P2
そして、同様のコマンドを子供たちに使用して終了します。
\draw (P2) -- node[right] {$N$} ++ (-60:2cm) node[below] {$0,0$};
\draw (P2) -- node[left] {$Y$} ++ (240:2cm) node[below] {$c-x,x$};
以上です。結果は次のとおりです。
完全なコード:
\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}
\draw (-2,-2) grid (2,2);
\node[circle,draw] (P1) {$P_1$};
\node[circle, draw, on grid, below = 2cm of P1, anchor=north] (P2) {$P_2$};
\draw (P1)-- node[right]{$x$} (P2);
\draw (P1) -- node[right] {$x=c$} ++ (-60:2cm) arc (-60:-120:2cm);
\draw (P1) -- node[left] {$x=0$} ++ (240:2cm);
\draw (P2) -- node[right] {$N$} ++ (-60:2cm) node[below] {$0,0$};
\draw (P2) -- node[left] {$Y$} ++ (240:2cm) node[below] {$c-x,x$};
\end{tikzpicture}
\end{document}
答え3
- 「空白」は単なる空のノードです。テキストを追加していないにもかかわらず、ノードが作成され、その結果空白が作成されます。これを解決するには、
coordinate
これらのノードのオプションに次のように追加するだけです。name=1, coordinate,...
- 次のポイントで解決しました。
- 角度については、オプション
calign=fixed edge angles
に追加しますfor tree={}
。 - マニュアルにも、これを行う方法が見つかりません。何か明らかなことを見逃していると思います。
いずれにせよ、現在の結果は次のとおりです。
答え4
編集:istgame
バージョン 2.0
とともにistgame
バージョン 2.0 では、ブランチの円弧型連続体をさらに制御できます。
\documentclass{standalone}
\usepackage{istgame}
\begin{document}
\begin{istgame}[->,font=\footnotesize]
\cntmdistance{15mm}{30mm}
\cntmAistb[->]{x=0}[al]{x=1}[ar]
\cntmApreset[ultra thin]<1.5>
\istrootcntmA(0)[null node]{1}
\istbA{x}[r]
\endist
\xtdistance{10mm}{20mm}
\istroot(1)(0-1)[null node]<45>{2}
\istb{Y}[l]{c-x,x}
\istb{N}[r]{0,0}
\endist
\end{istgame}
\end{document}
元の回答
ultimatum
これは、あなたが望むゲームの形を得るためのもう一つの解決策であり、istgame
パッケージです。(最後通牒ゲームを描画する別の方法は、パッケージ ドキュメントに記載されています。) 環境はistgame
とほぼ同じなので、環境内でtikzpicture
tikz を使用できます。macros
istgame
\documentclass{standalone}
\usepackage{istgame}
\begin{document}
\begin{istgame}[->,font=\footnotesize]
\istroot(0)[null node]{1}+15mm..15mm+
\istb{x=0}[l]
\istb<level distance=1.42*15mm>{x}[r]
\istb{x=c}[r]
\endist
\xtdistance{10mm}{20mm}
\istroot(1)(0-2)[null node]<45>{2}
\istb{Y}[l]{c-x,x}
\istb{N}[r]{0,0}
\endist
\draw[-,ultra thin,tension=1] plot [smooth] coordinates {(0-1)(0-2)(0-3)};
\end{istgame}
\end{document}