使用 TikZ 及其 \foreach 拟合点列表

使用 TikZ 及其 \foreach 拟合点列表

我有一个点列表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循环的全部功能(多个变量、选项等等countevaluate并不太难……

代码

\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}

此示例仅使用当前边界框。

相关内容