我想要使用 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}
它生成了以下图像:
第一个\node ... at (0.53,0.7)
只是指示我想要监视的区域。有两个问题显而易见:
- 与此相同的设置
\node
,当应用于时\spy
,结果会产生比最初请求的椭圆更小的椭圆 - 显然,由于使用了示波器,它
\spy
无法“看到”“下方”的任何图形(更准确地说,是图像像素),因此它似乎只放大了网格。
我可以使用这种在 内的\includegraphics
和的设置,但仍然可以获得以相对坐标表示的适当的 吗?如果可以,该如何表示?scope
tikzpicture
\spy
答案1
好的,我想我现在明白了——但希望最终有人会发布更正确的答案。无论如何,使用下面的代码,我现在得到了这个:
注意事项:
- 虽然
\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}
使用库提取单位x
和y
方向calc
这种技术。
关于你不能直接在坐标中使用(即硬编码)新坐标系的事实,这与工作\spy on
原理有关,\spy
这个答案表明定义在命令\coordinate
中使用新命名是“标准” \spy
。
还请注意,我没有费心将新的scope
坐标系定义调整到一般情况,即fig
节点具有非零内部分离,并且用户只希望拥有所(0,0)
包含(1,1)
图形的左下角和右上角。
奖励曲目
如果您经常需要这样做(例如,在beamer
演示文稿中显示不同幻灯片/叠加层上的不同放大部分),我会将代码提取到命令/环境中。只要发挥一点想象力,您就可以随心所欲地实现。以下内容可能是一个起点,我使用xparse
和etoolbox
定义了一个新环境,该环境旨在在内部使用,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}
德味
在上面的代码中,我选择不将要监视的坐标的创建委托给命令\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}