Hoy necesito nuevamente su ayuda para manejar el paquete forest
(es un paquete realmente poderoso, pero también bastante complejo). Básicamente, ya he pasado varias horas intentando dibujar un juego de ultimátum en forma de árbol que debería verse así:
Hasta ahora, he logrado crear este código que de alguna manera funciona (pero no funciona bien):
\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}
El resultado que obtengo es este:
Lo ideal es que las cosas que me gustaría modificar sean varias. Primero, me gustaría que (a) la línea curva toque las ramas del árbol, evitando ese feo espacio en blanco. En segundo lugar, (b) me gustaría evitar la superposición entre la línea curva y el nodo P2. En tercer lugar, (c) me gustaría asegurarme de que los ángulos de las dos ramas en los dos nodos sean los mismos. En otras palabras, el ángulo del segundo nodo es más amplio que el del primer nodo y me gustaría que fueran iguales. Cuarto, (d) me gustaría que las ramas del segundo nodo fueran más cortas que las del primero, si es posible. Todas las preguntas son igualmente relevantes. Cualquier ayuda con cualquiera de los cuatro problemas será muy apreciada.
PD: Idealmente, me gustaría ceñirme a mi código actual tanto como sea posible (en otras palabras, me gustaría aplicar los cambios mínimos necesarios al código para obtener los cambios que necesito porque este es un código que puedo entender). más o menos fácilmente).
Respuesta1
Aquí hay una solución que creo que cumple con todos los deseos establecidos. Supuse, a partir de la imagen de destino, que no quería que el arco llegara al punto más alto del nodo P_2
. Si eso no es necesario, no necesita molestarse con algunos de los cálculos involucrados en mi código.
\documentclass[tikz, border=10pt, multi]{standalone}
Cargamos bibliotecas TikZ through
y calc
para dibujar el arco correctamente.
\usetikzlibrary{through,calc}
\usepackage{forest}
Ahora configuramos algunos estilos. Algunos de estos simplifican su código existente. Si prefieres no utilizarlos, puedes omitirlos.
\forestset{%
auto edge label
automatiza el código para formatear el archivo edge label
. Crea el nodo, pone el contenido en modo matemático y decide si colocar la etiqueta a la izquierda o a la derecha del borde. Esto significa que
edge label=x^2, auto edge label
hará lo correcto.
auto edge label/.style={%
before typesetting nodes={%
No hagas nada si es el nodo raíz.
if level=0{}{
Si el nodo está en la segunda mitad de los hijos de su padre, o si es el hijo del medio...
if={n()>(n_children("!u")/2)}{
Si el nodo es el hijo del medio...
if={n()==((n_children("!u")+1)/2)}{
edge label/.wrap value={
node[midway, right] {$##1$}
},
}{
Si el nodo está en la segunda mitad de los hijos de su padre...
edge label/.wrap value={
node[midway, outer sep=1.5mm, right] {$##1$}
},
},
}{
Si el nodo está en la primera mitad de los hijos de su padre...
edge label/.wrap value={
node[midway, outer sep=1.5mm, left] {$##1$}
},
}
},
},
},
Este es el estilo para nodos vacíos más bonitos. Es de la página 65 del manual actual. Es parte de la linguistics
biblioteca. Por lo tanto, si usa esa biblioteca, puede omitir esta definición y simplemente aplicar el estilo listo para usar.
Tenga en cuenta que creo que la explicación en esta parte del manual se refiere erróneamente a opciones que no existen, pero no estoy seguro de esto.
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}
}
}{}
}
},
Este es el estilo para dibujar los arcos. Pasa esto como una opción al padre de los nodos a través de los cuales se debe dibujar el arco.
No se dibujan bordes como parte del árbol, excepto para el segundo hijo. Si hubiera más de 3 niños, esto tendría que volverse un poco más sofisticado. Este código asume 3 hijos.
En cambio, el arco se dibuja después calculando los puntos apropiados según la posición del nodo medio y el ángulo predeterminado de los nodos cuando se usa calign=fixed edge angles
. En este momento se dibujan los bordes del primer y tercer hijo.
[Esto realmente debería ser un poco más sofisticado en términos de verificar varias posibilidades, pero debería funcionar en casos relevantes como el 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 simple para mayor comodidad.
\tikzset{%
my circle/.style={draw, circle}
}
Luego podemos aplicar todo esto al árbol de la siguiente manera.
\begin{document}
\begin{forest}
for tree={
Del MWE.
l sep=4em,
s sep=8em,
Aplique dos de los nuevos estilos a todo el árbol.
auto edge label,
nice empty nodes,
Ponga todos los nodos en modo matemático para ahorrar signos de dólar.
math content,
}
Especifique el arc below
estilo de la raíz.
[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
Si desea aumentar aún más la separación entre los dos primeros niveles del árbol, la forma más sencilla es simplemente aumentar el valor de l sep
para el nodo raíz. He aquí un ejemplo 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}
Aquí la distancia mínima entre la raíz y sus hijos se establece en seis veces la distancia mínima habitual entre niveles con l sep*=6
. Si prefiere agregar una cantidad absoluta, podría decir l sep+=<dimension>
. O si simplemente desea anular el valor predeterminado, l sep=<dimension>
especifique la distancia mínima con precisión.
Es importante que lo quel sep
asegure sea lamínimodistancia. Entonces, si l sep
se establece una distancia muy pequeña para un nivel y un poco más grande para otro, es posible que termines con la misma distancia en cada caso porque otros factores harán quebosquenecesita que los nodos estén espaciados a distancias mayores que cualquiera de los mínimos especificados.
Observo que su árbol de destino real no es, de hecho, como el que mostró en la pregunta. De hecho, la parte más complicada de mi código anterior no es necesaria en absoluto para el árbol final.
Aquí hay una versión automatizada de ese árbol como referencia. Esta versión prescinde de las bibliotecas TikZ ya que sólo son necesarias para arc below
. arc through
es un nuevo estilo que conecta con los anclajes oeste y este en lugar de pasar por el ancla norte. my arc
determina el estilo del arco. Esto se puede configurar dentro del árbol usando my arcs={<key list>}
para determinar el estilo. Por defecto, está vacío y el arco se dibuja con el estilo de la edge
opción actual. Especificar el estilo con my arcs
permite complementar o anular el edge
estilo. Por ejemplo, el arco puede ser densely dashed
parejo si los bordes están dibujados sólidamente.
\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 A UN LADO
Esta versión es sólo paraClemente. Aunque tiene más de 546 caracteres, sigue siendo solo 644. Y Kile hace que el código exclusivo de TikZ tenga 563 caracteres, por lo que probablemente mis estadísticas cuenten los caracteres de manera diferente.
Personalmente, no creo que esto sea una ventaja, pero ahí lo tienes.
No es muy transparente, por lo que no recomiendo usarlo.
Sin embargo, la forma en que se dibuja el arco es mucho más clara que mi código anterior. Lo que probablemente haría es basarme arc below
en este método en lugar de usar la through
biblioteca.
El principal ahorro de caracteres se consigue eliminando la automatización. Las etiquetas de los bordes ya no se colocan automáticamente según la posición del niño en relación con sus hermanos. Entonces, si agrega un nodo, debe verificar si alguno de left
los s debe convertirse en right
s o viceversa. Además, no se utiliza ningún estilo para los círculos, lo que reduce la flexibilidad y la facilidad de mantenimiento del código. Finalmente, los signos de dólar se utilizan para el contenido de los nodos y no math content
porque math content,
contienen más caracteres que el número requerido para asignar un par de signos de dólar a cada nodo que los necesita.
Irónicamente, el dibujo del arco ahora utilizabosquepresenta más funciones y TikZ presenta menos. ( y()
se usa con un contenedor pgfmath para obtener la información necesaria para el arco, en lugar de depender de la 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}
Respuesta2
Sé que quieres seguir adelante, forest
pero mientras esperas una respuesta, puedes intentar comprender la forma en que un pobre dibuja tu plan. No es tan difícil:
Dibujemos P1
en alguna parte
\node[circle,draw] (P1) {$P_1$};
Dibujemos P2
a una cierta distancia conocida debajo.P1
\node[circle, draw, on grid, below = 2cm of P1, anchor=north] (P2) {$P_2$};
con on grid
y anchor=north
forzamos una 2cm
distancia entre el centro de P1 y el norte de P2. Lo necesitaremos para un arco perfecto.
Ahora podemos trazar la línea entre P1
yP2
\draw (P1)-- node[right]{$x$} (P2);
A continuación, decida qué ángulos desea para los niños izquierdo y derecho. La longitud del brazo comenzará 2cm
desde P1
el centro. Al final del niño de la derecha, dibujamos el arco porque conocemos el ángulo inicial, el ángulo final y el radio.
\draw (P1) -- node[right] {$x=c$} ++ (-60:2cm) arc (-60:-120:2cm);
\draw (P1) -- node[left] {$x=0$} ++ (240:2cm);
Y terminamos con P2
los niños usando comandos similares:
\draw (P2) -- node[right] {$N$} ++ (-60:2cm) node[below] {$0,0$};
\draw (P2) -- node[left] {$Y$} ++ (240:2cm) node[below] {$c-x,x$};
Eso es todo. El resultado:
y 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}
Respuesta3
- El "espacio en blanco" no es más que nodos vacíos. Aunque no haya agregado texto, los nodos se crean y, a su vez, crean el espacio en blanco. Para resolver esto, simplemente agregue
coordinate
a las opciones para esos nodos, comoname=1, coordinate,...
- Resuelto por el siguiente punto.
- Para los ángulos, agregue
calign=fixed edge angles
a susfor tree={}
opciones. - No encuentro una manera de hacer esto, ni siquiera en el manual. Creo que me estoy perdiendo algo obvio.
En cualquier caso, aquí está el resultado actual:
Respuesta4
Editar: istgame
versión 2.0
Con elistgame
En la versión 2.0, puede tener más controles sobre el tipo de arco continuo de ramas:
\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}
Respuesta original
Esta es otra solución para obtener la forma del ultimatum
juego que deseas, utilizando elistgame
paquete. (Puedes encontrar otra forma de dibujar el juego del ultimátum en el documento del paquete). Dado que el istgame
entorno es casi el mismo que el tikzpicture
, puedes usar tikz macros
dentro del istgame
entorno.
\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}