Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I have a list of N points (e.g. {(0,0),(42,7),...,(0,1)}). I want to use them as coordinates in TikZ, to draw some things. And I would like to fit them into a node (using TikZ's fit module (cf. http://www.texample.net/tikz/examples/feature/fit/ or PGF documentation §34) to put a label under them.

If I have, for example, 4 points, I can manually do it, and it works perfectly :

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

which gives :

alt text

Now I want to do the same for an arbitrary number of points. I hoped a \foreach would work:

\node (boxforeach) [draw=gray,dashed, inner sep=10pt,fit= \foreach \j in {0,1,...,3}{(point-\j) } ] {};

but all I get are lots of ! Package tikz Error: Cannot parse this coordinate..

Is there a mean to get that working? If there isn't, do you have a simple method to calculate the coordinates of the box manually?

share|improve this question
    
fixed your figure for you. –  Yossi Farjoun Oct 31 '10 at 22:04
add comment

4 Answers

up vote 15 down vote accepted

Well, I had hoped there'd be a nicer way to do it, but there's nothing in the TikZ manual, so this is what I'd do:

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

If you haven't seen \edef before, it's like \def or \newcommand, except it expands the definition before assigning it; \xdef is equivalent to \global\edef, so its effects can escape the foreach. Essentially, I just build up the list as you construct the points, and then use the result.

I also removed your manual counter and replaced it with the [count=\i] option to \foreach. \foreach actually has a lot of useful options; I recommend checking out section 51 of the TikZ & PGF manual.

share|improve this answer
    
+1 for the xdef explanation –  Christian Oct 31 '10 at 17:59
    
Going back to the manual counter solution ([count=\i] doesn't work with my version of pgf/tikz), it works perfectly. Thanks. –  stzagorec Oct 31 '10 at 19:53
    
I have posted a related question what is the count=\i –  Peter Grill Jun 25 '11 at 1:44
add comment

This is a hack of the fit library that should work in most cases (the only altered original macro is \tikz@lib@fit@scan to allow a \foreach parsing).

Note that the fit work is done inside a \foreach (see \tikz@scan@one@point\tikz@lib@fit@scan@handle@foreach@ in \tikz@lib@fit@scan@handle@foreach), so changes to dimensions must be made \global.

enter image description here

\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}
share|improve this answer
    
Note that a feature request has been added at the sourceforge project web page. –  cjorssen Mar 20 '12 at 11:59
add comment

Maybe a .List handler can be helpful in other cases, too?

The second TikZ picture shows cjorssen’s examples from his answer. Of course, with the .List handler, in all cases one would just do {(point-1),...,(point-<last>)} but I wanted to show how one would need to give an example that does not fit in the scheme.

Thoughts

PGFkeys offers with the .list handler already a \foreach alternative that doesn’t group its content (it actually does nearly the same), but what would be the benefit (besides checking whether \foreach is defined)? Maybe

I guess, it wouldn’t be too hard to implement a .foreach handler so that one could use the full power of a \foreach loop (multiple variables, options like count, evaluate and so on) …

Code

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

Output

enter image description hereenter image description here

share|improve this answer
add comment

Maybe this will do the job:

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

This example simply uses the current bounding box.

share|improve this answer
    
I looked a bit too quickly at your answer, it would also work in this case, but not if this is part of a bigger picture. However, there is a local bounding box option to {scope}'s. In my case, it would be more elegant, but it doesn't allow to "fit" points that are defined at different places. But thanks. –  stzagorec Oct 31 '10 at 20:09
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.