![仅在间谍放大镜中绘图](https://linux22.com/image/469193/%E4%BB%85%E5%9C%A8%E9%97%B4%E8%B0%8D%E6%94%BE%E5%A4%A7%E9%95%9C%E4%B8%AD%E7%BB%98%E5%9B%BE.png)
我希望能够在间谍放大镜中绘制某些内容,而不将其绘制在主图片中(例如,仅在放大的图片版本中添加特定注释)。
为什么这不是重复的?
相关话题:
- 如何仅在放大的区域中绘制某些东西(TikZ:间谍库)?尝试这样做,但没有进入 PGFPlots
axis
环境(也没有这个更多信息请参见下文。 - 带有放大镜的情节,其中有一个不同的情节用其他东西替换整个放大的区域,这不是我想要的。
- 如何用间谍仅在放大的区域中书写也不使用轴环境。
我想要实现的目标
理想情况下,我想在放大的绘图部分上指示某些内容,如下所示:
我设法用以下 MWE 制作了这个情节(灵感来自这个答案):
\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{width=10cm, height=5cm, compat=1.18}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\usetikzlibrary{spy}
% Store magnification and lens size in macros
\newcommand\magn{2}
\newcommand\msize{3cm}
\pgfdeclarelayer{fg} % declare foreground layer
\pgfsetlayers{main, fg}
\def\pival{3.14159265359}
\begin{document}
\begin{tikzpicture}[%
spy using outlines={circle, magnification=\magn, size=\msize, connect spies}
]
\begin{axis}[domain=0:2*\pival, ymin=0, ymax=1.2, xmin = 0, xmax=2*\pival, samples=17]
\addplot {sin(x*180/\pival)};
\coordinate (spyonto) at (axis cs:\pival/2,0.9);
\coordinate (magloc) at (axis cs:5,0.6);
\spy on (spyonto) in node at (magloc) [fill=white];
\coordinate (arrowstart) at (axis cs:4.3,0.5);
\coordinate (arrowend) at (axis cs:5.7, 0.5);
\end{axis}
\begin{pgfonlayer}{fg}
\draw[Stealth-Stealth, red, thick] (arrowstart) -- (arrowend);
\end{pgfonlayer}
\end{tikzpicture}
\end{document}
然而,这并不令人满意,原因如下:
- 箭头必须在
axis
环境之外绘制,这意味着如果我们想使用轴坐标,我们必须事先将它们声明为arrowstart
和arrowend
。 - 箭头必须在专用图层内绘制,这并不切实际。
- 必须根据大量参数(放大率和镜头尺寸、原始绘图坐标、放大镜位置)猜测/估计箭头坐标
另外,请记住,如果这些问题在这种精确情况下看起来像是一个小负担,那是因为它是一个涉及几个此类注释的复杂图形的过于简单的例子。
我探索的其他解决方案
使用所介绍的方法这里(注意小胡的评论),我得到了这个结果:
使用以下代码:
\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{width=10cm, height=5cm, compat=1.18}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\usetikzlibrary{spy, calc}
% Store magnification and lens size in macros
\newcommand\magn{2}
\newcommand\msize{3cm}
\pgfdeclarelayer{fg} % declare foreground layer
\pgfsetlayers{main, fg}
\def\pival{3.14159265359}
\tikzstyle{only in spy node}=[%
transform canvas={%
shift={($-\magn*(spyonto)+(magloc)$)},
scale=\magn,
}
]
\begin{document}
\begin{tikzpicture}[%
spy using outlines={circle, magnification=\magn, size=\msize, connect spies}
]
\begin{axis}[domain=0:2*\pival, ymin=0, ymax=1.2, xmin = 0, xmax=2*\pival, samples=17]
\begin{scope}
\addplot {sin(x*180/\pival)};
\coordinate (spyonto) at (axis cs:\pival/2,0.9);
\coordinate (magloc) at (axis cs:5,0.6);
\spy on (spyonto) in node at (magloc);% [fill=white];
\coordinate (arrowstart) at (axis cs:4.3,0.5);
\coordinate (arrowend) at (axis cs:5.7, 0.5);
\end{scope}
\begin{scope}[only in spy node]
% clip this scope to keep it inside the magnifier
\clip (spyonto) circle ({(\msize/2-0.4pt)/\magn});
\fill [green] (spyonto) circle (12pt);
\fill [red] (spyonto) circle (2pt);
\draw[Stealth-Stealth, red, thick] (1.2, 0.95) -- (2, 0.95);
\end{scope}
\end{axis}
\end{tikzpicture}
\end{document}
这在某种程度上是更好的,因为:
- 我们现在可以使用本机
axis
坐标来绘制注释(如果我们有想要准确指示的精确数据点,这很方便) - 我们不再使用单独的层
然而,这并没有完全发挥作用,因为注释现在在后面[fill=white];
主图。这就是为什么注释掉的原因,如果取消注释,所有注释都会消失。我尝试使用以下方法将它们放在前景层中:
\begin{pgfonlayer}{fg}
\clip (spyonto) circle ({(\msize/2-0.4pt)/\magn});
\fill [green] (spyonto) circle (12pt);
\fill [red] (spyonto) circle (2pt);
\draw[Stealth-Stealth, red, thick] (1.2, 0.95) -- (2, 0.95);
\end{pgfonlayer}
但如果我这样做,它们也会出现在微缩图中:
这不是理想的行为。
我也看了那里但因为我们想在放大镜里面画一些东西,所以我们不想离开它的观察范围。
如果您有解决此问题的方法,我们将非常欢迎!:)
答案1
该解决方案的目标是
- 无需处理间谍镜头选项,
- 不使用画布转换进行注释,
- 允许使用 PGFPlots 坐标系和
- 能够在放大的图片的前后进行绘画。
为了这,
\tikz@lib@spy@do
(这是完成所有事情的主要宏)被修补,以便在将侦测节点实际放到页面上之前找到它的位置,\tikz@lib@spy@transformation
在设置所需转换的注释之前添加宏。
这之前背景和背景背后spy-in 节点的路径用于绘制放大图片前后的内容(这实际上是一个path picture
[带有 PGFpicture 的节点],因此是背景路径——对于大多数节点来说,这是唯一定义的路径)。
您的 TikZ 图片需要进行的更改如下:
- 将间谍范围移至环境
axis
(间谍输入节点排版在间谍范围的末尾,这是我们访问 PGFPlots 坐标系的唯一机会), - 总是使用明确的坐标系(例如
axis cs:
, - 并将一切与间谍命令有关的内容放入
\pgfplotsextra
。
宏\spy
本身并没有做太多事情,只是收集它的参数并将它们放入一个列表中,以便在范围结束时执行,但此时对于 PGFPlots 的一些魔力来说已经太晚了。我们需要直接使用“执行”间谍代码\pgfplotsextra
。
注释应该被剪裁吗?也许可以重写,\tikz@lib@spy@do
但这是一个带有嵌套事物的转换节。我不知道并使用很多 PGFPlots 来深入研究这一点。
代码
\documentclass[tikz]{standalone}
\usepackage{pgfplots}
\pgfplotsset{width=10cm, height=5cm, compat=1.18}
\usetikzlibrary{arrows.meta, spy, calc}
\makeatletter
\let\tikz@lib@spy@do@orig\tikz@lib@spy@do % sigh
\def\tikz@lib@spy@do#1#2#3{% Have to find the location of spyinnode w/o drawing
\setbox\pgfutil@tempboxa\hbox{%
\node[overlay,spy annotations fg/.code=,spy annotations bg/.code=,
alias=tikzspyinnode]#3{};}%
\tikz@lib@spy@do@orig{#1}{#2}{#3}}
\tikzset{
spy annotations fg/.code={%
\tikz@addoption{%
\expandafter\edef\csname pgf@sh@fbg@\tikz@shape\endcsname{%
\noexpand\tikz@lib@spy@transformation{\tikz@shape}%
\pgfutil@unexpanded{#1}}}},
spy annotations bg/.code={%
\tikz@addoption{%
\expandafter\edef\csname pgf@sh@bbg@\tikz@shape\endcsname{%
\noexpand\tikz@lib@spy@transformation{\tikz@shape}%
\pgfutil@unexpanded{#1}}}}}
\def\tikz@lib@spy@transformation#1{% what shape really uses these paths?
\expandafter\let\csname pgf@sh@fbg@#1\endcsname\pgfutil@undefined % nesting
\expandafter\let\csname pgf@sh@bbg@#1\endcsname\pgfutil@undefined % nesting
\pgftransformreset
\let\tikz@transform\relax
\pgftransformshift{%
\expandafter\tikzset\expandafter{\tikz@lib@spy@lens}%
\pgftransforminvert
\pgfpointdiff {\pgfpointanchor{tikzspyonnode}{center}}
{\pgfpointtransformed{\pgfpointanchor{tikzspyinnode}{center}}}}%
\expandafter\tikzset\expandafter{\tikz@lib@spy@lens}}
\makeatother
\begin{document}
\begin{tikzpicture}[declare function={sind(\x)=sin(deg(\x));}]
\begin{axis}[
domain=0:2*pi, ymin=0, ymax=1.2, xmin = 0, xmax=2*pi, samples=17,
spy using outlines={circle, magnification=2, size=3cm, connect spies}]% ←
\addplot {sind(x)};
\pgfplotsextra{% ← !
\coordinate (magloc) at (axis cs:5, 0.6); % needs axis cs:
\spy on (axis cs:pi/2,0.9) in node at (magloc) [% needs axis cs:
spy annotations fg={% these all need explicit cs:
\draw[Stealth-Stealth, red, thick]
(axis cs: pi/2-pi/8, {sind(pi/2-pi/8)}) -- +(axis direction cs: pi/4, 0);
},
spy annotations bg={% yeah, these too
\fill [green] (axis cs: pi/2, .9) circle[radius=12pt];
\fill [red] (axis cs: pi/2, .9) circle[radius= 2pt];
\node[below left, inner sep=+0pt, draw, dashed, overlay, align=center]
at (tikzspyinnode) {I'm not clipped\\and also a circle.};
% tikzspyinnode only works because of the patch
}
];
}
\end{axis}
\end{tikzpicture}
\end{document}
输出
答案2
瞧:
\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{width=10cm, height=5cm, compat=1.18}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\usetikzlibrary{spy, calc}
% Store magnification and lens size in macros
\newcommand\magn{2}
\newcommand\msize{3cm}
\pgfdeclarelayer{fg} % declare foreground layer
\pgfsetlayers{main, fg}
\def\pival{3.14159265359}
\tikzstyle{only in spy node}=[%
transform canvas={%
shift={($-\magn*(spyonto)+(magloc)$)},
scale=\magn,
}
]
\begin{document}
\begin{tikzpicture}[spy using outlines={}]
\begin{axis}[domain=0:2*\pival, ymin=0, ymax=1.2, xmin = 0, xmax=2*\pival, samples=17]
\coordinate (spyonto) at (axis cs:\pival/2,0.9);
\coordinate (magloc) at (axis cs:5,0.6);
\coordinate (arrowstart) at (axis cs:4.3,0.5);
\coordinate (arrowend) at (axis cs:5.7, 0.5);
\begin{scope}[spy using outlines={circle, magnification=\magn, size=\msize, connect spies}]
\addplot {sin(x*180/\pival)};
\spy on (spyonto) in node at (magloc)[name=spynode];
\end{scope}
% Remplace pink par white, j'ai mis pink pour que tu te rendes mieux compte sur la figure.
\fill [pink] (magloc) circle (\msize/2);
\begin{scope}[anchor=spyonto, transform canvas={%
shift={($-\magn*(spyonto)+(magloc)$)},
scale=\magn }]
% clip this scope to keep it inside the magnifier
\clip (spyonto) circle ({(\msize/2-0.4pt)/\magn});
\fill [green] (spyonto) circle (12pt);
\fill [red] (spyonto) circle (2pt);
\draw[Stealth-Stealth, red, thick] (1.2, 0.95) -- (2, 0.95);
\end{scope}
\end{axis}
\end{tikzpicture}
\end{document}