使用 TikZ spy 放大图片的一部分,以相对范围坐标表示

使用 TikZ spy 放大图片的一部分,以相对范围坐标表示

我想要使用 TikZ 间谍库放大图片的一部分;但是,使用相对坐标的范围如下#9561 使用 TikZ 在图像上绘图. 假设我使用图片tux.png- 考虑这个MWE:

\documentclass[%
  12pt,
  journal,
  onecolumn,
  twoside,
  draftcls,
  letterpaper,
]{IEEEtran}

% wget https://i.stack.imgur.com/aVGcn.png -O tux.png

\usepackage[nopar]{lipsum}
\usepackage{xstring}
% https://tex.stackexchange.com/a/26808/2595
\makeatletter
\def\unpacklipsum#1#2#3{%
  \count@=#1\relax
  \advance\count@\m@ne
  \def#3{}%
  \loop\ifnum\count@<#2\relax
    \advance\count@\@ne
    \edef#3{#3\csname lipsum@\romannumeral\count@\endcsname}%
  \repeat}
\makeatother
% https://tex.stackexchange.com/a/168754/2595
\def\loremnchars[#1]#2{%
  \unpacklipsum{#1}{#1}{\myunpacked}%
  \StrMid{\myunpacked}{1}{#2}% same as \StrLeft{\myunpacked}{#2}
}


\usepackage{caption}
\usepackage{tikz}
\usetikzlibrary{shapes}
\usetikzlibrary{spy}

\begin{document}

\begin{center}
  \begin{tikzpicture}[
    spy using outlines={%
      draw=red,
      ellipse,
      red,
      magnification=2,
      connect spies
    }
  ]
    \node [draw=black, anchor=south west, inner sep=0pt] (myimg) at (0,0) {
      \includegraphics[width=0.9\textwidth]{tux.png}
    };
    \begin{scope}[
      x={(myimg.south east)},y={(myimg.north west)},
      spy using outlines={%
        draw=red,
        ellipse,
        red,
        magnification=2,
        connect spies
      }
    ]
      \draw[help lines,xstep=.1,ystep=.1] (0,0) grid (1,1);
      \node[
        draw=red,ellipse,line width=1pt,minimum width=0.9cm,minimum height=5.25cm
      ]
      at (0.53,0.7) {};
      \spy[
        draw=red,ellipse,line width=1pt,minimum width=0.9cm,minimum height=5.25cm
      ]
      on (0.53,0.7) in node [left] at (0.8,0.825);
    \end{scope}
  \end{tikzpicture}
  \captionof{figure}[justmy]{Just my image; \loremnchars[5]{255} ...}
  \label{fig:test}
\end{center}

\end{document}

它生成了以下图像:

测试71.png

第一个\node ... at (0.53,0.7)只是指示我想要监视的区域。有两个问题显而易见:

  • 与此相同的设置\node,当应用于时\spy,结果会产生比最初请求的椭圆更小的椭圆
  • 显然,由于使用了示波器,它\spy无法“看到”“下方”的任何图形(更准确地说,是图像像素),因此它似乎只放大了网格。

我可以使用这种在 内的\includegraphics和的设置,但仍然可以获得以相对坐标表示的适当的 吗?如果可以,该如何表示?scopetikzpicture\spy

答案1

好的,我想我现在明白了——但希望最终有人会发布更正确的答案。无论如何,使用下面的代码,我现在得到了这个:

测试72.png

注意事项:

  • 虽然\node可以(并且确实)按照 调整自身以适应相对坐标scope\spy但显然不能(并且不会)
    • 但是,\spy可以引用节点,因此我们可以设置命名\node和/或\coordinate(相对于scope相对坐标),并将它们作为输入提供\spy
  • 显然,输入的尺寸选项\spy是针对最终的“ in node”显示,而不是针对“偷看者” on(节点);因此,要修改“偷看者”的尺寸,我们必须将尺寸选项乘以放大倍数
  • 仅包含spy using outlines在被监视的元素/“层”上(即,如果将其添加到scope,则图像的像素将不可用;它只应添加到tikzpicture,因为它是“托管”\includegraphics及其像素的元素/“层”)。

考虑到这一点,下面是 OP 中 MWE 的修改部分,使其工作(只有document没有前言的部分,因为只在那里进行了更改)。请注意,这\magnif是现在存储放大倍数的地方;并且由于\spy似乎限制了长度的可扩展性,因此长度改为\def'd(并且\global\def是 'd,否则\spy看不到它们);最后,有了这一切,现在我不知道是什么控制了line width“窥视器”了(但我可以控制指示器节点的线宽,效果也一样好)。无论如何,这是代码:

\begin{document}

\global\def\magnif{2}

\begin{center}
  \begin{tikzpicture}[
    spy using outlines={%
      draw=blue,
      ellipse,
      red,
      magnification=\magnif,
      line width=2pt,
      connect spies
    }
  ]
    \node [draw=black, anchor=south west, inner sep=0pt] (myimg) at (0,0) {
      \includegraphics[width=0.9\textwidth]{tux.png}
    };
    \begin{scope}[
      x={(myimg.south east)},y={(myimg.north west)},
      % enabling spy using outlines here makes the pixels unreadable; so skip it:
    ]
      \draw[help lines,xstep=.1,ystep=.1] (0,0) grid (1,1);
      % must have \global for \spy!
      % lengths may not be expandable: http://tex.stackexchange.com/a/18302/2595
      % better use \edef here, in case you need {0.5\somelength}, else \spy will not pick it up
      \global\edef\mw{0.9cm} %\newlength{\mw}\setlength{\mw}{0.9cm}
      \global\edef\mh{5.25cm} %\newlength{\mh}\setlength{\mh}{5.25cm}
      \node[
        draw=red,ellipse,line width=1pt,minimum width=\mw,minimum height=\mh,
      ]
      (lookhere) at (0.53,0.7) {};
      \coordinate (lookherenext) at (0.8,0.825);
      % line width here has no effect;
      % also, coordinates for \spy are not scaled!
      \spy[
        draw=green,ellipse,line width=4pt,minimum width=\magnif*\mw,minimum height=\magnif*\mh,
      ]
      %on (0.53,0.7) in node [left] at (0.8,0.825);
      on (lookhere) in node [left] at (lookherenext);
    \end{scope}
  \end{tikzpicture}
  \captionof{figure}[justmy]{Just my image; \loremnchars[5]{255} ...}
  \label{fig:test}
\end{center}

\end{document}

答案2

我不是 PGF 专家,但我认为您自己提出的解决方案还不算太完善。我会让它更容易使用,即

  • 调整scope新的坐标系,使其在引用未放置的节点时也能工作at (0,0)
  • 更重要的是,让使用与坐标系相同的单位来指定要放大区域的大小scope,即“纯”数字(通常为 0 到 1)。

这是根据您的回答构建的 MWE。请注意,我稍微更改了要监视的坐标以及要监视的区域的宽度和高度,以便更直接、更直观地解释这些尺寸的含义。

\documentclass[tikz, border=1mm]{standalone}
\usetikzlibrary{calc, shapes, spy}

\begin{document}
    \begin{tikzpicture}[spy using outlines={%
                            ellipse,
                            red,
                            line width=2pt,
                            connect spies
                        }]
        \node[inner sep=0mm] (fig) at (0,0){\includegraphics{tux}};
        %Scope relative to the image to be spied on
        \begin{scope}[x={($(fig.south east) - (fig.south west)$)},
                      y={($(fig.north west) - (fig.south west)$)}, shift={(fig.south west)}]
            %Extract units in x and y directions as length
            \path let \p{P}=(1,0), \n{xlencm}={scalar(veclen(\x{P},\y{P})/1cm)}
                  in \pgfextra{\xdef\ux{\n{xlencm}cm}};
            \path let \p{P}=(0,1), \n{ylencm}={scalar(veclen(\x{P},\y{P})/1cm)}
                  in \pgfextra{\xdef\uy{\n{ylencm}cm}};
            %Add a grid to pick up areas to zoom on
            \draw[help lines, step=0.1] (0,0) grid (1.0001,1.0001);
            \coordinate (lookherenext) at (0.6,0.3);
            \coordinate (lookhere) at (0.5,0.7);
            %Auxiliary macros
            \gdef\magnif{2}
            \gdef\mw{0.4} % These are now in the figure coordinate
            \gdef\mh{0.2} % system, which makes it easier to spy
            %Actual spy command
            \spy[magnification=\magnif,
                 minimum width=\magnif*\mw*\ux,
                 minimum height=\magnif*\mh*\uy] on (lookhere) in node at (lookherenext);
        \end{scope}
    \end{tikzpicture}
\end{document}

MWE-基础版

使用库提取单位xy方向calc这种技术

关于你不能直接在坐标中使用(即硬编码)新坐标系的事实,这与工作\spy on原理有关,\spy这个答案表明定义在命令\coordinate中使用新命名是“标准” \spy

还请注意,我没有费心将新的scope坐标系定义调整到一般情况,即fig节点具有非零内部分离,并且用户只希望拥有所(0,0)包含(1,1)图形的左下角和右上角。

奖励曲目

如果您经常需要这样做(例如,在beamer演示文稿中显示不同幻灯片/叠加层上的不同放大部分),我会将代码提取到命令/环境中。只要发挥一点想象力,您就可以随心所欲地实现。以下内容可能是一个起点,我使用xparseetoolbox定义了一个新环境,该环境旨在在内部使用,tikzpicture以及一个新命令,该命令旨在在自定义环境中使用。环境有一个*变体,可以在您想要监视的节点上绘制指导线。它旨在在“绘制”时使用,然后在监视准备就绪后将其删除。

\documentclass[tikz, border=1mm]{standalone}
\usepackage{xparse, etoolbox}
\usetikzlibrary{calc, shapes, spy}

\makeatletter
%Tikz scope to spy on node with relative coordinates
\NewDocumentEnvironment{internal@SpyOnNode}{ s m O{} }
{
    \begin{scope}[x={($(#2.south east) - (#2.south west)$)},
                  y={($(#2.north west) - (#2.south west)$)}, shift={(#2.south west)}, #3]
        \IfBooleanTF{#1}
        {
            \draw[help lines, xstep=0.05,ystep=0.05] (0,0) grid (1.001,1.001);
            \draw[very thin,  xstep=0.1,ystep=0.1] (0,0) grid (1.001,1.001);
            \pgfkeys{/pgf/number format/precision=1}
            \foreach \x in {0,0.1,...,1.001} {
                \node [anchor=north, font=\tiny] at (\x,0) {\rotatebox{-90}{\pgfmathroundtozerofill{\x}\pgfmathresult}};
                \node [anchor=south, font=\tiny] at (\x,1) {\rotatebox{90}{\pgfmathroundtozerofill{\x}\pgfmathresult}};
                \node [anchor=east, font=\tiny]  at (0,\x) {\pgfmathroundtozerofill{\x}\pgfmathresult};
                \node [anchor=west, font=\tiny]  at (1,\x) {\pgfmathroundtozerofill{\x}\pgfmathresult};
            }
        }{}
        %Extract units in x and y directions as length
        \path let \p{P}=(1,0), \n{xlencm}={scalar(veclen(\x{P},\y{P})/1cm)}
              in \pgfextra{\xdef\ux{\n{xlencm}cm}};
        \path let \p{P}=(0,1), \n{ylencm}={scalar(veclen(\x{P},\y{P})/1cm)}
              in \pgfextra{\xdef\uy{\n{ylencm}cm}};
}
{
    \end{scope}
}
\NewDocumentEnvironment{SpyOnNode}{ m O{} }
  {\begin{internal@SpyOnNode}{#1}[#2]}
  {\end{internal@SpyOnNode}}
\NewDocumentEnvironment{SpyOnNode*}{ m O{} }
  {\begin{internal@SpyOnNode}*{#1}[#2]}
  {\end{internal@SpyOnNode}}

%Command meant to be used in the environment above (\ux and \uy defined)
% #1 -> magnification factor
% #2 -> width  of area to be magnified
% #3 -> height of area to be magnified
% #4 -> name of the coordinate at which to spy on
% #5 -> name of the coordinate at which the magnification is placed
\NewDocumentCommand{\spyarea}{m m m m m }{%
    \spy[magnification=#1,
         minimum width=#1*#2*\ux,
         minimum height=#1*#3*\uy] on (#4) in node at (#5);
}
\makeatother


\begin{document}
    \begin{tikzpicture}[spy using outlines={%
                            ellipse,
                            red,
                            line width=2pt,
                            connect spies
                        }]
        \node[inner sep=0mm] (fig) at (0,0){\includegraphics{tux}};
        \begin{SpyOnNode*}{fig} % Remove the * when the picture is finalised
            \path coordinate (from1) at (0.50,0.70)
                  coordinate (from2) at (0.15,0.35)
                  coordinate (to1)   at (0.6,0.2)
                  coordinate (to2)   at (0.1,0.7);
            \spyarea{2}{0.4}{0.2}{from1}{to1}
            \spyarea{2}{0.1}{0.1}{from2}{to2}
        \end{SpyOnNode*}        
    \end{tikzpicture}
\end{document}

MWE-高级

德味

在上面的代码中,我选择不将要监视的坐标的创建委托给命令\spyarea,因为您可能希望稍后重用这些坐标。如果这不是您的需要,下面的版本更容易使用,因为坐标的创建隐藏在命令内部。但是,要做到这一点,这些坐标的名称必须在每次调用时更改,因为

\spy命令不会立即创建节点。相反,这些节点的创建被推迟到\spy使用该命令的侦察范围的末尾。这是必要的,因为为了在包含放大版本的节点内重复整个范围,整个图片需要在创建此节点时可用。

\spy为此,我使用了 PGF 随机数,但例如也可以使用计数器。此外,在展开时发出带有完全展开的坐标名称的命令至关重要\spy。要了解有关所用技术的更多信息,您可以参考此有用的答案

\documentclass[tikz, border=1mm]{standalone}
\usepackage{xparse, etoolbox}
\usetikzlibrary{calc, shapes, spy}

\makeatletter
%Tikz scope to spy on node with relative coordinates
\NewDocumentEnvironment{internal@SpyOnNode}{ s m O{} }
{
    \begin{scope}[x={($(#2.south east) - (#2.south west)$)},
                  y={($(#2.north west) - (#2.south west)$)}, shift={(#2.south west)}, #3]
        \IfBooleanTF{#1}
        {
            \draw[help lines, xstep=0.05,ystep=0.05] (0,0) grid (1.001,1.001);
            \draw[very thin,  xstep=0.1,ystep=0.1] (0,0) grid (1.001,1.001);
            \pgfkeys{/pgf/number format/precision=1}
            \foreach \x in {0,0.1,...,1.001} {
                \node [anchor=north, font=\tiny] at (\x,0) {\rotatebox{-90}{\pgfmathroundtozerofill{\x}\pgfmathresult}};
                \node [anchor=south, font=\tiny] at (\x,1) {\rotatebox{90}{\pgfmathroundtozerofill{\x}\pgfmathresult}};
                \node [anchor=east, font=\tiny]  at (0,\x) {\pgfmathroundtozerofill{\x}\pgfmathresult};
                \node [anchor=west, font=\tiny]  at (1,\x) {\pgfmathroundtozerofill{\x}\pgfmathresult};
            }
        }{}
        %Extract units in x and y directions as length
        \path let \p{P}=(1,0), \n{xlencm}={scalar(veclen(\x{P},\y{P})/1cm)}
              in \pgfextra{\xdef\ux{\n{xlencm}cm}};
        \path let \p{P}=(0,1), \n{ylencm}={scalar(veclen(\x{P},\y{P})/1cm)}
              in \pgfextra{\xdef\uy{\n{ylencm}cm}};
}
{
    \end{scope}
}
\NewDocumentEnvironment{SpyOnNode}{ m O{} }
  {\begin{internal@SpyOnNode}{#1}[#2]}
  {\end{internal@SpyOnNode}}
\NewDocumentEnvironment{SpyOnNode*}{ m O{} }
  {\begin{internal@SpyOnNode}*{#1}[#2]}
  {\end{internal@SpyOnNode}}

%Command meant to be used in the environment above (\ux and \uy defined)
% #1 -> magnification factor
% #2 -> width  of area to be magnified
% #3 -> height of area to be magnified
% #4 -> coordinates at which to spy on
% #5 -> coordinates at which the magnification is placed
\NewDocumentCommand{\spyarea}{m m m m m}{%
    \pgfmathgeneratepseudorandomnumber
    \let\myprn\pgfmathresult
    \coordinate (tmp@from@\myprn) at #4;
    \coordinate (tmp@to@\myprn) at #5;
    \expanded{%
        \noexpand\spy[magnification=#1,
                      minimum width=#1*#2*\ux,
                      minimum height=#1*#3*\uy] on (tmp@from@\myprn) in node at (tmp@to@\myprn);
     }
}
\makeatother


\begin{document}
    \begin{tikzpicture}[spy using outlines={%
                            ellipse,
                            red,
                            line width=2pt,
                            connect spies
                        }]
        \node[inner sep=0mm] (fig) at (0,0){\includegraphics{tux}};
        \begin{SpyOnNode*}{fig} % Remove the * when the picture is finalised
            \spyarea{2}{0.4}{0.2}{(0.50,0.70)}{(0.6,0.2)}
            \spyarea{2}{0.1}{0.1}{(0.15,0.35)}{(0.1,0.7)}
        \end{SpyOnNode*}        
    \end{tikzpicture}
\end{document}

相关内容