Контекст:Этот вопрос является продолжениемЛучшая практика создания изображений TikZ с вложенными элементами.Символ 1предоставилОтветна этот вопрос, где \scopenode
определено.
\scopenode
области видимости превращены в узлы, т. е. можно: назвать область видимости с помощью name=foo
; позиционировать область видимости с помощью at=(somewhere)
; и настраивать позицию с помощью anchor=something
. Они в основном потрясающие, так как их можно вкладывать.
Затем вКак сделать использование tikzexternalize и saveboxes совместимым?,cfrпредоставилОтветулучшение этих \scopenode
s путем включения отображения как \scopenode
фона , так и содержимого \scopenode
. (Фон Scopenode действительно будет нарисованвыше(В противном случае содержание.)
Проблема:Я попытался включить \scopenode
в TiкZ. matrix
Однако у меня есть некоторые вопросы:
При использовании решения Symbol 1
\scopenode
элементы позиционируются правильно, но их содержимое не отображается, поскольку оно скрыто за цветом заливки.
Благодаря решению CFR контент отображается (и хорошо позиционирован), но
\scopenode
все портится.
Вопрос:Как сделать \scopenode
совместимым с TiкЗ matrix
?
MWE
(Пример создает матрицу содинстрока и два столбца. В обеих ячейках (A1 и B1) закрашен-нарисован scopenope. A (красно-оранжевый) закреплен на юге, а B (желто-зеленый) закреплен на севере. В каждом scopenode нарисован путь от (0,0) до (1,1)).
_______
| A | |
|---|---| <-- baseline
|___|_B_|
С решением Symbol 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}
С решением 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}
решение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
\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}
Информация об импорте
Дорогой будущий я:
Для вашего сведения, содержимое матрицы набирается только один раз в hbox. А затем они перемещаются в соответствующую ячейку. А затем вся матрица перемещается в нужную позицию. Первое перемещение выполняется , \pgf@matrix@shift@nodes@initial
а второе — \pgf@matrix@shift@nodes@secondary
. Они просто применяются \pgf@shift@node
к списку узлов. Чтобы зарегистрировать scopenode, вы добавили строку
\expandafter\pgf@nodecallback\expandafter{\tikz@scopenode@name@smuggle}%
поэтому узел области действия также перемещается.
В настоящее время все в scopenode будет набрано дважды. Для вложенного scopenode все набрано дважды по глубине . Это действительно раздражает. Может быть, кто-то сможет улучшить это, кстати TiкZ имеет дело с матрицей.
(Однако матрица не может быть вложенной. Вы выиграли!)
Кроме того, вы изменили
\global\let\tikz@fig@name\tikz@fig@name
к
\global\let\tikz@fig@name@\tikz@fig@name
так что имя области видимости не может быть доступно из другого места.
В частности, ТикZ будет применяться \pgf@shift@node
к самой матрице. Если матрица не имеет имени, то последний scopenode будет смещен, что нежелательно. Вы потратили два часа только на то, чтобы найти этот глупый баг. ИЗВЛЕКИТЕ УРОК.
Кроме того, вы жестко закодировали фоновый путь узла области видимости, так что теперь он заполняется/рисуется до содержимого области видимости. (Отсюда и название)фонpath) Но расчет длинный и, похоже, избыточный. Надеюсь, кто-нибудь сможет его улучшить.
Тем не менее, вы избежали использования pgfonlayer
. Это здорово.