我有一个点列表N
(例如{(0,0),(42,7),...,(0,1)}
)。我想将它们用作 TikZ 中的坐标,以绘制一些东西。我想将fit
它们放入一个节点(使用 TikZ 的fit
模块(参见http://www.texample.net/tikz/examples/feature/fit/或 PGF 文档 §34) 在其下方贴上标签。
例如,如果我有 4 个点,我可以手动完成,而且效果很好:
\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{calc,fit}
\begin{document}
\begin{tikzpicture}
\newcounter{i}
\setcounter{i}{0}
\foreach \point in
{(0,0),(0,2),(2,0),(2,2)}
{
\node[coordinate] (point-\arabic{i}) at \point { };
\fill (point-\arabic{i}) circle (0.1);
\stepcounter{i}
}
\node (box) [draw=gray,dashed, inner sep=10pt,fit=(point-0) (point-1) (point-2) (point-3)] {};
\node (label) at (box.south) [below] { blah };
\end{tikzpicture}
\end{document}
这使 :
现在我想对任意数量的点执行相同操作。我希望\foreach
能够奏效:
\node (boxforeach) [draw=gray,dashed, inner sep=10pt,fit= \foreach \j in {0,1,...,3}{(point-\j) } ] {};
但我得到的只是很多! Package tikz Error: Cannot parse this coordinate.
。
有没有什么办法可以实现这个功能?如果没有,你有没有简单的方法来手动计算盒子的坐标?
答案1
好吧,我曾希望有一个更好的方法来做到这一点,但是 TikZ 手册中没有任何内容,所以这就是我要做的:
\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{calc,fit}
\begin{document}
\begin{tikzpicture}
\edef\points{}
\foreach \point [count=\i] in {(0,0),(0,2),(2,0),(2,2)} {
\def\this{point-\i}
\node[coordinate] (\this) at \point {} ;
\fill (\this) circle (0.1) ;
\xdef\points{(\this) \points}
}
\node (box) [draw=gray,dashed, inner sep=10pt,fit=\points] {};
\node (label) at (box.south) [below] { blah };
\end{tikzpicture}
\end{document}
如果您以前没见过\edef
,它类似于\def
或\newcommand
,只不过它在分配之前会扩展定义;\xdef
相当于\global\edef
,因此其效果可以逃避 foreach。本质上,我只是在您构造点时构建列表,然后使用结果。
我还删除了您的手动计数器,并用选项替换它[count=\i]
。\foreach
实际上 \foreach
有很多有用的选项;我建议查看 TikZ 和 PGF 手册的第 51 节。
答案2
这是对 fit 库的一种破解,在大多数情况下都可以工作(唯一改变的原始宏是\tikz@lib@fit@scan
允许\foreach
解析)。
请注意,配合工作是在内部完成的\foreach
(参见\tikz@scan@one@point\tikz@lib@fit@scan@handle@foreach@
)\tikz@lib@fit@scan@handle@foreach
,因此必须对尺寸进行更改\global
。
\documentclass[border=.5cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{fit}
\makeatletter
\def\tikz@lib@fit@scan{%
\pgfutil@ifnextchar\pgf@stop{\pgfutil@gobble}{%
\pgfutil@ifnextchar\foreach{\tikz@lib@fit@scan@handle@foreach}{%
\tikz@scan@one@point\tikz@lib@fit@scan@handle}}}
\def\tikz@lib@fit@scan@handle@foreach\foreach#1in#2#3{%
\foreach #1 in {#2}
{\tikz@scan@one@point\tikz@lib@fit@scan@handle@foreach@#3}
\tikz@lib@fit@scan}
\def\tikz@lib@fit@scan@handle@foreach@#1{%
\iftikz@shapeborder
\tikz@lib@fit@adjust{%
\pgfpointanchor{\tikz@shapeborder@name}{west}}%
\tikz@lib@fit@adjust{%
\pgfpointanchor{\tikz@shapeborder@name}{east}}%
\tikz@lib@fit@adjust{%
\pgfpointanchor{\tikz@shapeborder@name}{north}}%
\tikz@lib@fit@adjust{%
\pgfpointanchor{\tikz@shapeborder@name}{south}}%
\else
\tikz@lib@fit@adjust{#1}%
\fi
\global\pgf@xa=\pgf@xa
\global\pgf@ya=\pgf@ya
\global\pgf@xb=\pgf@xb
\global\pgf@yb=\pgf@yb}
\makeatletter
\begin{document}
\begin{tikzpicture}
\foreach \point [count=\i] in {(0,0),(0,2),(2,0),(2,2),(3,3),(-1,-1)}{%
\node[coordinate] (point-\i) at \point {};
\fill (point-\i) circle (0.1);}
\node [draw=gray,dashed,inner sep=10pt,
fit = \foreach \j in {1,2,...,4}{(point-\j) }] (boxforeach1) {};
\node [draw=red,dashed, inner sep=10pt,
fit = (point-1) \foreach \j in {2,...,5}{(point-\j) }] (boxforeach2) {};
\node [draw=blue,dashed, inner sep=10pt,
fit = (point-1) \foreach \j in {2,...,5}{(point-\j) } (point-6)]
(boxforeach3) {};
\end{tikzpicture}
\end{document}
答案3
也许.List
处理程序在其他情况下也会有帮助?
第二张 TikZ 图片显示cjorssen 的来自的例子他的回答当然,使用.List
处理程序,在所有情况下都可以这样做,{(point-1),...,(point-<last>)}
但我想说明如何需要给出一个不适合该方案的示例。
思考
PGFkeys.list
已为处理程序提供了一种\foreach
不分组内容的替代方案(实际上它的作用几乎相同),但这样做有什么好处(除了检查是否\foreach
已定义)?也许
.foreach
我猜,实现一个处理程序以便能够充分利用\foreach
循环的全部功能(多个变量、选项等等count
)evaluate
并不太难……
代码
\documentclass[tikz]{standalone}
\usetikzlibrary{fit}
\makeatletter
\pgfqkeys{/handlers}{%
.List/.code={%
\let\pgfkeys@global@temp\pgfutil@empty
\foreach \pgfkeys@temp in{#1}{
\ifx\pgfkeys@global@temp\pgfutil@empty
\global\let\pgfkeys@global@temp\pgfkeys@temp
\else
\expandafter\pgfutil@g@addto@macro\expandafter\pgfkeys@global@temp\expandafter
{\pgfkeys@temp}%
\fi}%
\expandafter\pgfkeys@exp@call\expandafter{\pgfkeys@global@temp}}}
\begin{document}
\begin{tikzpicture}
\foreach \point[count=\cnt from 0] in {(0,0),(0,2),(2,0),(2,2)}
\fill \point circle[radius=.1] coordinate (point-\cnt);
\node[label=below:blah, draw=gray, dashed, inner sep=+10pt,
fit/.List={(point-0),(point-...),(point-3)}] {};
\end{tikzpicture}
\begin{tikzpicture}[nodes={draw, dashed, inner sep=+10pt}]
\foreach \point [count=\cnt] in {(0,0), (0,2), (2,0), (2,2), (3,3), (-1,-1)}
\fill \point circle[radius=.1] coordinate (point-\cnt);
\node[gray, fit/.List={(point-1),(point-...),(point-4)}] {};
\node[red, fit/.List={(point-1),(point-2),(point-...),(point-5)}] {};
\node[blue, fit/.List={(point-1),(point-2),(point-...),(point-5),(point-6)}] {};
\end{tikzpicture}
\end{document}
输出
答案4
也许这个可以完成这个工作:
\begin{tikzpicture}
\newcounter{i}
\setcounter{i}{0}
\foreach \point in
{(0,0),(0,2),(2,0),(2,2)}
{
\node[coordinate] (point-\arabic{i}) at \point { };
\fill (point-\arabic{i}) circle (0.1);
\stepcounter{i}
}
\path (current bounding box.south west) -- (current bounding box.south east)
node[midway, below] {blah};
\end{tikzpicture}
此示例仅使用当前边界框。