Información de importación

Información de importación

Contexto:Esta pregunta es una continuación deMejores prácticas para crear imágenes TikZ con elementos anidados.Símbolo 1proporcionóuna respuestaa esta pregunta, donde \scopenodese define.

\scopenodeson ámbitos convertidos en nodos, es decir, se puede: nombrar el ámbito mediante name=foo; coloque el alcance por at=(somewhere); y ajuste la posición mediante anchor=something. Son básicamente increíbles, ya que se pueden anidar.

Entonces, en¿Cómo hacer compatible el uso de tikzexternalize y saveboxes?,cfrproporcionóuna respuestamejorando estos \scopenodeal permitir la visualización del \scopenodefondo y el contenido del archivo \scopenode. (El fondo de Scopenode de hecho se dibujaríaarribael contenido de otra manera.)

Problema:Intenté incluir \scopenodeen un TikZ. matrix​Sin embargo, tengo algunos problemas:

  • Con la solución del Símbolo 1, \scopenodelos s están bien posicionados, pero su contenido no aparece porque está oculto detrás del color de relleno.
    ingrese la descripción de la imagen aquí

  • Con la solución de CFR, el contenido se muestra (y está bien posicionado), pero \scopenodese estropea.
    ingrese la descripción de la imagen aquí

Pregunta:Cómo hacer \scopenodecompatible con Tik¿Z matrix?


MWE
(El ejemplo crea una matriz conunofila y dos columnas. En ambas celdas (A1 y B1), se rellena y dibuja un telescopio. A (rojo-naranja) está anclado al sur y B (amarillo-verde) está anclado al norte. En cada nodo de alcance, se dibuja una ruta de (0,0) a (1,1)).

 _______
| A |   |
|---|---|  <-- baseline
|___|_B_|

Con la solución del Símbolo 1:

\documentclass{article} 
\usepackage{tikz} 
\usetikzlibrary{matrix} 
\usetikzlibrary{backgrounds} 
% \usetikzlibrary{external} 
% \tikzexternalize 
% \tikzset{external/prefix=build/} 

\makeatletter 
\newbox\tikz@sand@box 
\newcount\tikz@scope@depth 
\tikz@scope@depth111\relax 
\def\scopenode[#1]#2{% 
\begin{pgfinterruptboundingbox}% 
\advance\tikz@scope@depth111\relax% 
% process the user option 
\begin{scope}[name=tempscopenodename,at={(0,0)},anchor=center,#1]% 
% try to extract positioning information: name, at, anchor 
\global\let\tikz@fig@name\tikz@fig@name% 
\global\let\tikz@node@at\tikz@node@at% 
\global\let\tikz@anchor\tikz@anchor% 
\end{scope}% 
\let\tikz@scopenode@name\tikz@fig@name% 
\let\tikz@scopenode@at\tikz@node@at% 
\let\tikz@scopenode@anchor\tikz@anchor% 
% try to typeset this scope 
% we only need bounding box information 
% the box itself will be discard 
\setbox\tikz@sand@box=\hbox{% 
\begin{scope}[local bounding box=tikz@sand@box\the\tikz@scope@depth,#1]% 
#2% 
\end{scope}% 
}% 
% goodbye. haha 
\setbox\tikz@sand@box=\hbox{}% 
% now typeset again 
\begin{scope}[local bounding box=\tikz@scopenode@name]% 
% use the bounding box information to reposition the scope 
\pgftransformshift{\pgfpointanchor{tikz@sand@box\the\tikz@scope@depth}{\tikz@scopenode@anchor}% 
\pgf@x-\pgf@x\pgf@y-\pgf@y}% 
\pgftransformshift{\tikz@scopenode@at}% 
\begin{scope}[#1]% 
#2 
\end{scope}% 
\end{scope}% 
\pgfkeys{/pgf/freeze local bounding box=\tikz@scopenode@name}% 
\global\let\tikz@scopenode@name@smuggle\tikz@scopenode@name% 
\end{pgfinterruptboundingbox}% 
% make up the bounding box 
\path(\tikz@scopenode@[email protected] west)(\tikz@scopenode@[email protected] east);% 
% draw something, not necessary 
\draw[#1](\tikz@scopenode@[email protected] west)rectangle(\tikz@scopenode@[email protected] east);% 
} 
\makeatother 
\begin{document} 
\begin{tikzpicture}[ 
remember picture, 
inner sep=0pt, 
outer sep=0pt, 
] 
\draw [help lines](-2,-2) grid (2,2); 
\matrix[ 
column sep=2em, 
row sep = 1em, 
nodes in empty cells, 
anchor=center, 
nodes={anchor=center}, 
] 
{ 
\scopenode[draw = red, fill = orange, anchor=south] { 
\draw [blue] (0,0) -- (1,1); 
}; 
& 
\scopenode[draw = yellow, fill = green, anchor=north] { 
\draw [black] (0,1) -- (1,0); 
}; 
\\ 
}; 
\end{tikzpicture} 
\end{document}

Con la solución de CFR:

\documentclass{article} 
\usepackage{tikz} 
\usetikzlibrary{matrix} 
\usetikzlibrary{backgrounds} 
% \usetikzlibrary{external} 
% \tikzexternalize 
% \tikzset{external/prefix=build/} 

\makeatletter 
\pgfdeclarelayer{scopenode} 
\pgfsetlayers{background,scopenode,main} 
\tikzset{% 
% adapted from tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarybackgrounds.code.tex 
on scopenode layer/.style={% 
execute at begin scope={% 
\pgfonlayer{scopenode}% 
\let\tikz@options=\pgfutil@empty% 
\tikzset{every on scopenode layer/.try,#1}% 
\tikz@options% 
}, 
execute at end scope={\endpgfonlayer} 
}, 
} 
% ateb Symbol 1: tex.stackexchange.com/a/… 
\newbox\tikz@sand@box 
\newcount\tikz@scope@depth 
\tikz@scope@depth111\relax 
\def\scopenode[#1]#2{% name=<enw>, at=<man>, anchor=<angor> 
\begin{pgfinterruptboundingbox}% 
\advance\tikz@scope@depth111\relax% 
% process the user option 
\begin{scope}[name=tempscopenodename,at={(0,0)},anchor=center,#1]% 
% try to extract positioning information: name, at, anchor 
\global\let\tikz@fig@name\tikz@fig@name% 
\global\let\tikz@node@at\tikz@node@at% 
\global\let\tikz@anchor\tikz@anchor% 
\end{scope}% 
\let\tikz@scopenode@name\tikz@fig@name% 
\let\tikz@scopenode@at\tikz@node@at% 
\let\tikz@scopenode@anchor\tikz@anchor% 
% try to typeset this scope 
% we only need bounding box information 
% the box itself will be discard 
\setbox\tikz@sand@box=\hbox{% 
\begin{scope}[local bounding box=tikz@sand@box\the\tikz@scope@depth,#1]% 
#2% 
\end{scope}% 
}% 
% goodbye. haha 
\setbox\tikz@sand@box=\hbox{}% 
% now typeset again 
\begin{scope}[local bounding box=\tikz@scopenode@name]% 
% use the bounding box information to reposition the scope 
\pgftransformshift{\pgfpointanchor{tikz@sand@box\the\tikz@scope@depth}{\tikz@scopenode@anchor}% 
\pgf@x-\pgf@x\pgf@y-\pgf@y}% 
\pgftransformshift{\tikz@scopenode@at}% 
\begin{scope}[#1]% 
#2 
\end{scope}% 
\end{scope}% 
\pgfkeys{/pgf/freeze local bounding box=\tikz@scopenode@name}% 
\global\let\tikz@scopenode@name@smuggle\tikz@scopenode@name% 
\end{pgfinterruptboundingbox}% 
% make up the bounding box 
\path(\tikz@scopenode@[email protected] west)(\tikz@scopenode@[email protected] east);% 
% draw something, not necessary 
\begin{scope}[on scopenode layer]% 
\draw[#1](\tikz@scopenode@[email protected] west)rectangle(\tikz@scopenode@[email protected] east);% 
\end{scope}% 
} 
\makeatother 
\begin{document} 
\begin{tikzpicture}[ 
remember picture, 
inner sep=0pt, 
outer sep=0pt, 
] 
\draw [help lines](-2,-2) grid (2,2); 
\matrix[ 
column sep=2em, 
row sep = 1em, 
nodes in empty cells, 
anchor=center, 
nodes={anchor=center}, 
] 
{ 
\scopenode[draw = red, fill = orange, anchor=south] { 
\draw [blue] (0,0) -- (1,1); 
}; 
& 
\scopenode[draw = yellow, fill = green, anchor=north] { 
\draw [black] (0,1) -- (1,0); 
}; 
\\ 
}; 
\end{tikzpicture} 
\end{document}

Respuesta1

Esto es de lejos lo mejor que puedo conseguir:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{matrix}
\usetikzlibrary{backgrounds}
% \usetikzlibrary{external}
% \tikzexternalize
% \tikzset{external/prefix=build/}

\makeatletter
    \newbox\tikz@sand@box
    \newcount\tikz@scope@depth
    \newdimen\tikz@scope@shiftx
    \newdimen\tikz@scope@shifty
    \newdimen\tikz@scope@swx
    \newdimen\tikz@scope@swy
    \newdimen\tikz@scope@nex
    \newdimen\tikz@scope@ney
    \tikz@scope@depth111\relax
    \def\scopenode[#1]#2{%
        \begin{pgfinterruptboundingbox}%
            \advance\tikz@scope@depth111\relax%
            % process the user option
            \begin{scope}[name=tempscopenodename,at={(0,0)},anchor=center,#1]%
                % try to extract positioning information: name, at, anchor
                \global\let\tikz@fig@name@\tikz@fig@name%
                \global\let\tikz@node@at@\tikz@node@at%
                \global\let\tikz@anchor@\tikz@anchor%
            \end{scope}%
            \let\tikz@scopenode@name\tikz@fig@name@%
            \let\tikz@scopenode@at\tikz@node@at@%
            \let\tikz@scopenode@anchor\tikz@anchor@%
            % try to typeset this scope
            % we only need bounding box information
            % the box itself will be discard
            \setbox\tikz@sand@box=\hbox{%
                \begin{scope}[local bounding box=tikz@sand@box\the\tikz@scope@depth,#1]%
                    #2%
                \end{scope}%
            }%
            % goodbye. haha
            \setbox\tikz@sand@box=\hbox{}%
            % now typeset again
            \begin{scope}[local bounding box=\tikz@scopenode@name]%
                % use the bounding box information to reposition the scope
                \pgfpointanchor{tikz@sand@box\the\tikz@scope@depth}{\tikz@scopenode@anchor}%
                \tikz@scope@shiftx-\pgf@x%
                \tikz@scope@shifty-\pgf@y%
                \tikz@scopenode@at%
                \advance\tikz@scope@shiftx\pgf@x%
                \advance\tikz@scope@shifty\pgf@y%
                \pgftransformshift{\pgfpoint{\tikz@scope@shiftx}{\tikz@scope@shifty}}
                % the background path
                % lengthy, tedious calculation
                % someone please improve this
                \pgfpointanchor{tikz@sand@box\the\tikz@scope@depth}{south west}
                \tikz@scope@swx\pgf@x\advance\tikz@scope@swx\tikz@scope@shiftx
                \tikz@scope@swy\pgf@y\advance\tikz@scope@swy\tikz@scope@shifty
                \pgfpointanchor{tikz@sand@box\the\tikz@scope@depth}{north east}
                \tikz@scope@nex\pgf@x\advance\tikz@scope@nex\tikz@scope@shiftx
                \tikz@scope@ney\pgf@y\advance\tikz@scope@ney\tikz@scope@shifty
                \path(\tikz@scope@swx,\tikz@scope@swy)coordinate(tempsw)
                     (\tikz@scope@nex,\tikz@scope@ney)coordinate(tempne);
                \path[#1](tempsw)rectangle(tempne);
                % typeset the content for real
                \begin{scope}[#1]%
                    #2%
                \end{scope}%
            \end{scope}%
            \pgfkeys{/pgf/freeze local bounding box=\tikz@scopenode@name}%
            \global\let\tikz@scopenode@name@smuggle\tikz@scopenode@name%
        \end{pgfinterruptboundingbox}%
        % make up the bounding box
        \path(\tikz@scopenode@[email protected] west)(\tikz@scopenode@[email protected] east);%
        % compatible code for matrix
        \expandafter\pgf@nodecallback\expandafter{\tikz@scopenode@name@smuggle}%
    }
\makeatother



\begin{document}
    \begin{tikzpicture}[remember picture,inner sep=0pt,outer sep=0pt]
        \draw[help lines](-2,-2)grid(2,2);
        \matrix()
        [
            column sep=2em,
            row sep=1em,
            nodes in empty cells,
            anchor=center,
            nodes={anchor=center},
        ]
        {
            \scopenode[draw=red,fill=orange,name=aaa,anchor=south] {
                \draw[blue](0,0)--(1,1)circle(.2);
            };
            &
            \scopenode[draw=yellow,fill=green,name=bbb,anchor=north] {
                \draw[black](0,1)--(1,0)circle(.1);
            };
            &
            \scopenode[fill=cyan,name=ccc,anchor=east,scale=.8] {
                \draw(0,0)--(1,1)circle(.3)--(2,0);
            };
            \\
            \node(aaaa){};
            &
            \node(bbbb){};
            &
            \node(cccc){};
            \\
        };
    \draw[->](2,2)node[above]{this is the orange scopenode}to[bend left](aaa.east);
    \draw[->](-2,-2)node[below]{this is the green scopenode}to[bend left](bbb.west);
    \draw[->](3,-1)node[right]{this is the cyan scopenode}to[bend left](ccc.south);
    \end{tikzpicture}
\end{document}

Información de importación

Querido futuro yo:

Para su información, el contenido de la matriz se escribe solo una vez en un hbox. Y luego se trasladan a la celda correspondiente. Y luego toda la matriz se mueve a la posición deseada. El primer movimiento lo realiza \pgf@matrix@shift@nodes@initialy el segundo por \pgf@matrix@shift@nodes@secondary. Simplemente se aplican \pgf@shift@nodea una lista de nodos. Para registrar el nodo de alcance, agregó la línea

\expandafter\pgf@nodecallback\expandafter{\tikz@scopenode@name@smuggle}%

entonces el nodo de alcance también se mueve.


Actualmente, todo lo que esté en el nodo de alcance se escribirá dos veces. Para el nodo de alcance anidado, las cosas se escriben 2 veces en profundidad . Esto es realmente frustrante. Tal vez alguien pueda mejorar esto por cierto Ti.kZ trata con la matriz.

(Sin embargo, la matriz no se puede anidar. ¡Tú ganas!)



Además, cambiaste

\global\let\tikz@fig@name\tikz@fig@name

a

\global\let\tikz@fig@name@\tikz@fig@name

para que no se pueda acceder al nombre del nodo de alcance en ningún otro lugar.

En particular, TikZ se aplicará \pgf@shift@nodea la propia matriz. Si la matriz no tiene nombre, el último nodo de alcance se desplazará, lo cual no es deseado. Pasaste dos horas sólo para encontrar este estúpido error. APRENDE LA LECCIÓN.



Además, codificó la ruta de fondo del nodo de alcance para que ahora se complete/dibuje antes del contenido del alcance. (De ahí el nombrefondocamino) Pero el cálculo es largo y aparentemente redundante. Espero que alguien pueda mejorarlo.

Aún así evitaste usar pgfonlayer. Eso es genial.

información relacionada