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 just played with an answer to a question I asked here some time ago: Divide a rectangle into n times k equal cells. I wanted to be able to generate the list of points in a separate macro, however if I just put the list into a macro for example

\newcommand{\testlist}{1/2}

And do

\rectDiv{7}{5}{(1,1)}{(4,3)}{\testlist}

I get the following:

rectangle

So it seems that 1/2 is interpreted as the coordinate (0.5,0.5). If I have something like 2/0 I get an compiler error because of division by zero.

Why is \newcommand{\testlist}{1/2} \rectDiv{7}{5}{(1,1)}{(4,3)}{\testlist}not equivalent to \rectDiv{7}{5}{(1,1)}{(4,3)}{1/2} ?

How do I have to modify the example such that I can put the list of points into a macro which will be the last argument of \rectDiv?

I also tried \def and \edef instead of \newcommand without success.

share|improve this question
2  
In the rectDiv macro, remove the braces around #5 i.e. replace that line with \foreach \i/\j in #5 { and it would work. –  percusse Dec 19 '11 at 14:18
    
@percusse Thanks, that works, however see my comment to MartinScharrer's answer below. –  student Dec 19 '11 at 18:55
1  
You can't have a \foreach loop to generate a list for another \foreach loop. It isn't fully expandable and therefore breaks inside the \edef. –  Martin Scharrer Dec 19 '11 at 21:06

1 Answer 1

up vote 6 down vote accepted

If you use \foreach \i/\j in {1/2} { .. } then the \foreach macro can see the separator / and then splits 1 and 2. However, if both are hidden inside a macro like \testlist (defined to be 1/2) it does not find the separator and assumes that only a single value is used. Therefore \testlist is takes as \i and \j as empty.
You need to expand \testlist first, or use \foreach \i/\j in \testlist, because \foreach is defined to accept a macro instead of the { } argument.

Expanding would work as follows:

\edef\@tempa{\noexpand\foreach \noexpand\i/\noexpand\j in {\testlist, ..}}
\@tempa { <loop content> }

which works with multiple list macros and assumes \makeatletter and \makeatother around the code.


Full example:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
  \def\rectDiv#1#2#3#4#5{%#columns, #rows, rectangle start, rectangle end, list of elements to fill
    \begin{tikzpicture}
      \draw #3 rectangle #4;
      \path #3;
      \pgfgetlastxy{\firstx}{\firsty}
      \path #4;
      \pgfgetlastxy{\secondx}{\secondy}
      \pgfmathsetlengthmacro{\xdiff}{\secondx-\firstx}
      \pgfmathsetlengthmacro{\ydiff}{\secondy-\firsty}
      \pgfmathsetlengthmacro{\myxstep}{\xdiff/#1}
      \pgfmathsetlengthmacro{\myystep}{\ydiff/#2}
      \foreach \x in {1,...,#1}{
        \draw ($#3 +\x*(\myxstep,0)$) -- ($#3 +(0,\ydiff) +\x*(\myxstep,0)$);
      }
      \foreach \y in {1,...,#2}{
        \draw ($#3 +\y*(0,\myystep)$) -- ($#3 +(\xdiff,0) +\y*(0,\myystep)$);
      }
      \edef\temp{\noexpand\foreach \noexpand\i/\noexpand\j in {#5}}
      \temp{
        \path[fill=blue!20,draw] ($#3 + (\i*\myxstep,\j*\myystep)$) rectangle ($#3 + (\i*\myxstep,\j*\myystep) + (\myxstep,\myystep)$);
      }
    \end{tikzpicture}
  }
\begin{document}
  \rectDiv{7}{5}{(1,1)}{(4,3)}{0/0,1/1,2/0,5/3}

  \def\list{1/0}
  \rectDiv{7}{5}{(1,1)}{(4,3)}{\list}

  \rectDiv{7}{5}{(1,1)}{(4,3)}{\list,2/0,5/3}

\end{document}

Or alternatively:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
  \def\rectDiv#1#2#3#4#5{%#columns, #rows, rectangle start, rectangle end, list of elements to fill
    \begin{tikzpicture}
      \draw #3 rectangle #4;
      \path #3;
      \pgfgetlastxy{\firstx}{\firsty}
      \path #4;
      \pgfgetlastxy{\secondx}{\secondy}
      \pgfmathsetlengthmacro{\xdiff}{\secondx-\firstx}
      \pgfmathsetlengthmacro{\ydiff}{\secondy-\firsty}
      \pgfmathsetlengthmacro{\myxstep}{\xdiff/#1}
      \pgfmathsetlengthmacro{\myystep}{\ydiff/#2}
      \foreach \x in {1,...,#1}{
        \draw ($#3 +\x*(\myxstep,0)$) -- ($#3 +(0,\ydiff) +\x*(\myxstep,0)$);
      }
      \foreach \y in {1,...,#2}{
        \draw ($#3 +\y*(0,\myystep)$) -- ($#3 +(\xdiff,0) +\y*(0,\myystep)$);
      }
      \edef\list{#5}
      \foreach \i/\j in \list {
        \path[fill=blue!20,draw] ($#3 + (\i*\myxstep,\j*\myystep)$) rectangle ($#3 + (\i*\myxstep,\j*\myystep) + (\myxstep,\myystep)$);
      }
    \end{tikzpicture}
  }
\begin{document}
  \rectDiv{7}{5}{(1,1)}{(4,3)}{0/0,1/1,2/0,5/3}

  \def\alist{1/0}
  \rectDiv{7}{5}{(1,1)}{(4,3)}{\alist}

  \def\blist{5/3}
  \rectDiv{7}{5}{(1,1)}{(4,3)}{\alist,2/0,\blist}

\end{document}
share|improve this answer
    
Thanks. Your second suggestions seems to be the same as percusses comment. This works very well, however then you have to give rectDiv a macro like \teslist as argument and cannot give the list directly anymore. I don't really understand your first suggestion. Could you please provide a complete example for this and add some comments? Is it possible that rectDiv accepts both a macro and list directly as an argument? –  student Dec 19 '11 at 18:54
    
@user4011: I added two full examples. As I stated you need to fully expand the list first to flatten all value pairs hidden inside macros so that the / separator is on the top level and can be seen by \foreach. –  Martin Scharrer Dec 19 '11 at 19:41
1  
@user4011 You are modifying the question continuously. Please ask a new question such that one can follow the argument in a complete manner. We would keep helping as much as we can but it is getting tedious each time you write up a new detail. Having said that, you must know the very basics of TeX or LaTeX to get a feeling what Martin is recommending in his very nice answer. The reason why it doesn't work is crucially linked to how functions and loops are cascaded in (La)TeX. It might sound awkward at first but they serve a good purpose. I recommend you to read a little about expanding. –  percusse Dec 20 '11 at 4:01

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.