Hoje, preciso novamente da sua ajuda para lidar com o pacote forest
(é um pacote muito poderoso, mas também bastante complexo). Basicamente, já passei várias horas tentando desenhar um jogo de ultimato em forma de árvore que deveria ficar assim:
Até agora, consegui criar este código que de alguma forma funciona (mas não funciona bem):
\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 saída que obtenho é esta:
Idealmente, as coisas que gostaria de modificar são várias. Primeiro, gostaria que (a) a linha curva tocasse os galhos da árvore, evitando aquele espaço branco feio. Segundo, (b) gostaria de evitar a sobreposição entre a linha curva e o nó P2. Terceiro, (c) gostaria de ter certeza de que os ângulos dos dois ramos nos dois nós são os mesmos. Em outras palavras, o ângulo do segundo nó é maior que o do primeiro nó e gostaria que fossem iguais. Quarto, (d) gostaria que os ramos do segundo nó fossem mais curtos que os do primeiro, se possível. Todas as questões são igualmente relevantes. Qualquer ajuda com qualquer uma das quatro questões será muito apreciada.
PS: Idealmente, eu gostaria de manter meu código atual o máximo possível (em outras palavras, gostaria de aplicar as alterações mínimas necessárias ao código para obter as alterações necessárias, porque este é um código que posso entender mais ou menos facilmente).
Responder1
Aqui está uma solução que acho que atende a todos os desejos declarados. Presumi, a partir da imagem de destino, que você não queria que o arco ficasse aquém do ponto mais alto do nó P_2
. Se isso não for necessário, você não precisa se preocupar com alguns dos cálculos envolvidos no meu código.
\documentclass[tikz, border=10pt, multi]{standalone}
Carregamos bibliotecas through
e calc
TikZ para obter o arco desenhado corretamente.
\usetikzlibrary{through,calc}
\usepackage{forest}
Agora configuramos alguns estilos. Algumas delas simplificam seu código existente. Se preferir não utilizá-los, você pode omiti-los.
\forestset{%
auto edge label
automatiza o código para formatar o arquivo edge label
. Ele cria o nó, coloca o conteúdo no modo matemático e decide se coloca o rótulo à esquerda ou à direita da borda. Isso significa que
edge label=x^2, auto edge label
fará a coisa certa.
auto edge label/.style={%
before typesetting nodes={%
Não faça nada se for o nó raiz.
if level=0{}{
Se o nó estiver na segunda metade dos filhos dos pais, ou se for o filho do meio ...
if={n()>(n_children("!u")/2)}{
Se o nó for o filho do meio...
if={n()==((n_children("!u")+1)/2)}{
edge label/.wrap value={
node[midway, right] {$##1$}
},
}{
Se o nó estiver na segunda metade dos filhos dos pais ...
edge label/.wrap value={
node[midway, outer sep=1.5mm, right] {$##1$}
},
},
}{
Se o nó estiver na primeira metade dos filhos dos pais ...
edge label/.wrap value={
node[midway, outer sep=1.5mm, left] {$##1$}
},
}
},
},
},
Este é o estilo para nós vazios mais agradáveis. É da página 65 do manual atual. Faz parte da linguistics
biblioteca. Portanto, se você usar essa biblioteca, poderá omitir essa definição e apenas aplicar o estilo pronto para uso.
Observe que acho que a explicação nesta parte do manual se refere erroneamente a opções inexistentes, mas não tenho certeza sobre isso.
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}
}
}{}
}
},
Aqui está o estilo para desenhar os arcos. Você passa isso como uma opção para o pai dos nós através dos quais o arco deve ser desenhado.
Nenhuma aresta é desenhada como parte da própria árvore, exceto para o segundo filho. Se houver mais de três filhos, isso precisará ser um pouco mais sofisticado. Este código assume 3 filhos.
Em vez disso, o arco é desenhado posteriormente calculando os pontos apropriados com base na posição do nó do meio e no ângulo padrão para os nós ao usar calign=fixed edge angles
. As bordas do primeiro e do terceiro filho são desenhadas neste momento.
[Isso realmente deveria ser um pouco mais sofisticado em termos de verificação de várias possibilidades, mas deve funcionar em casos relevantes como o 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},
}
}
}
Estilo TikZ simples para maior conveniência.
\tikzset{%
my circle/.style={draw, circle}
}
Então podemos simplesmente aplicar tudo isso à árvore da seguinte maneira.
\begin{document}
\begin{forest}
for tree={
Do MWE.
l sep=4em,
s sep=8em,
Aplique dois dos novos estilos à árvore inteira.
auto edge label,
nice empty nodes,
Coloque todos os nós no modo matemático para salvar cifrões.
math content,
}
Especifique o arc below
estilo da raiz.
[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}
Código completo:
\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}
EDITAR
Se você quiser aumentar ainda mais a separação entre os dois primeiros níveis da árvore, a maneira mais fácil é simplesmente aumentar o valor de l sep
para o nó raiz. Aqui está um exemplo deliberadamente exagerado:
\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}
Aqui, a distância mínima entre a raiz e seus filhos é definida como seis vezes a distância mínima usual entre os níveis com l sep*=6
. Se preferir adicionar um valor absoluto, você poderia dizer l sep+=<dimension>
. Ou se você quiser apenas substituir o padrão, l sep=<dimension>
especifique a distância mínima com precisão.
É importante que o que l sep
garanta seja amínimodistância. Portanto, se l sep
for definido muito pequeno para um nível e um pouco maior para outro, você poderá acabar com a mesma distância em cada caso, porque outros fatores significarão queflorestaprecisa que os nós sejam espaçados em distâncias maiores do que qualquer um dos mínimos especificados.
Observo que sua árvore de destino real não é, de fato, como a que você mostrou na pergunta. Na verdade, a parte mais complicada do meu código acima não é necessária para a sua árvore final.
Aqui está uma versão automatizada dessa árvore para referência. Esta versão dispensa as bibliotecas TikZ, pois elas são necessárias apenas para arquivos arc below
. arc through
é um novo estilo que se conecta às âncoras oeste e leste, em vez de passar pela âncora norte. my arc
determina o estilo do arco. Isso pode ser definido na árvore usando my arcs={<key list>}
para determinar o estilo. Por padrão, está vazio e o arco é desenhado com o estilo da edge
opção atual. Especificar o estilo my arcs
permite complementar ou substituir o edge
estilo. Por exemplo, o arco pode ser densely dashed
uniforme se as arestas forem desenhadas de forma sólida.
\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}
EDITAR AO LADO
Esta versão é apenas paraClemente. Embora tenha mais de 546 caracteres, ainda tem apenas 644. E o Kile faz o código somente do TikZ com 563 caracteres, então provavelmente minhas estatísticas estão contando caracteres de forma diferente.
Pessoalmente, não acho que isso seja uma vantagem, mas aí está.
Não é muito transparente, então não recomendo usar isso.
No entanto, a forma como o arco é desenhado é muito mais organizada do que no meu código anterior. O que eu provavelmente faria seria basear-me arc below
nesse método, em vez de usar a through
biblioteca.
A principal economia de caracteres é obtida com a eliminação da automação. Os rótulos das bordas não são mais colocados automaticamente de acordo com a posição do filho em relação aos seus irmãos. Portanto, se você adicionar um nó, deverá verificar se algum left
s deve se tornar right
s ou vice-versa. Além disso, nenhum estilo é utilizado para os círculos, reduzindo a flexibilidade e a facilidade de manutenção do código. Finalmente, os cifrões são usados para o conteúdo dos nós, e não math content
porque math content,
contêm mais caracteres do que o número necessário para atribuir um par de cifrões a cada nó que precisa deles.
Ironicamente, o desenho do arco agora usaflorestaapresenta mais recursos e TikZ apresenta menos recursos. ( y()
é usado com um wrapper pgfmath para obter as informações necessárias para o arco, em vez de depender da through
biblioteca.)
\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}
Responder2
Eu sei que você quer continuar, forest
mas enquanto espera por uma resposta, você pode tentar entender a maneira de um homem pobre traçar seu esquema. Não é tão difícil:
Vamos desenhar P1
em algum lugar
\node[circle,draw] (P1) {$P_1$};
Vamos desenhar P2
a uma certa distância conhecida abaixoP1
\node[circle, draw, on grid, below = 2cm of P1, anchor=north] (P2) {$P_2$};
com on grid
e anchor=north
forçamos uma 2cm
distância entre o centro de P1 e o norte de P2. Precisaremos disso para um arco perfeito.
Agora podemos traçar a linha entre P1
eP2
\draw (P1)-- node[right]{$x$} (P2);
Em seguida, decida quais ângulos você deseja para os filhos esquerdo e direito. O comprimento do braço começará 2cm
do P1
centro. No final do filho direito, desenhamos o arco porque sabemos o ângulo inicial, o ângulo final e o raio.
\draw (P1) -- node[right] {$x=c$} ++ (-60:2cm) arc (-60:-120:2cm);
\draw (P1) -- node[left] {$x=0$} ++ (240:2cm);
E terminamos com P2
as crianças usando comandos semelhantes:
\draw (P2) -- node[right] {$N$} ++ (-60:2cm) node[below] {$0,0$};
\draw (P2) -- node[left] {$Y$} ++ (240:2cm) node[below] {$c-x,x$};
Isso é tudo. O resultado:
e código completo:
\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}
Responder3
- O “espaço em branco” nada mais é do que nós vazios. Mesmo que você não tenha adicionado texto, os nós são criados, criando, por sua vez, o espaço em branco. Para resolver isso, basta adicionar
coordinate
opções para esses nós, comoname=1, coordinate,...
- Resolvido no próximo ponto.
- Para os ângulos, adicione
calign=fixed edge angles
às suasfor tree={}
opções. - Não consigo encontrar uma maneira de fazer isso, nem mesmo no manual. Acho que estou perdendo algo óbvio.
De qualquer forma, aqui está o resultado atual:
Responder4
Editar: istgame
versão 2.0
Com oistgame
versão 2.0, você pode ter mais controles no tipo de arco contínuo de ramificações:
\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}
Resposta original
Esta é mais uma solução para obter a forma de ultimatum
jogo desejada, utilizando oistgame
pacote. (Você pode encontrar outra maneira de desenhar o jogo do ultimato no documento do pacote.) Como o istgame
ambiente é quase o mesmo que o tikzpicture
, você pode usar o tikz macros
dentro do istgame
ambiente.
\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}