3

I am running Ubuntu version 11.04 with PGF package version 2.00-1. I have trouble running this sample code posted to How to generate all possible Venn diagrams (with the case below) efficiently?

\documentclass{standalone}
%\url{https://tex.stackexchange.com/q/67395/86}
\usepackage{tikz}

\makeatletter

\def\venn@strip#1#2\venn@STOP{%
  \def\venn@next{#1}%
  \gdef\venn@rest{#2}%
}

\newcommand{\venn}[1]{%
\begin{tikzpicture}
\coordinate (A) at (0,0);
\coordinate (B) at (2,0);
\coordinate (C) at (1,{sqrt(3)});
\coordinate (S-SE) at (5,-3);
\coordinate (S-NW) at (-3,{sqrt(3)+3});
  \edef\venn@rest{#100000000}%
  \foreach \i in {0,...,7} {
  \begin{scope}[even odd rule]
    \expandafter\venn@strip\venn@rest\venn@STOP
    \ifnum\venn@next=1\relax
    \pgfmathparse{Mod(\i,2) == 1 ? "(S-SE) rectangle (S-NW)" : ""}
    \path[clip] \pgfmathresult (A) circle[radius=2];
    \pgfmathparse{Mod(floor(\i/2),2) == 1 ? "(S-SE) rectangle (S-NW)" : ""}
    \path[clip] \pgfmathresult (B) circle[radius=2];
    \pgfmathparse{Mod(floor(\i/4),2) == 1 ? "(S-SE) rectangle (S-NW)" : ""}
    \path[clip] \pgfmathresult (C) circle[radius=2];
    \fill[rounded corners,red] (S-SE) rectangle (S-NW);
    \fi
  \end{scope}
  }
    \draw[ultra thick] (A) circle[radius=2];
    \draw[ultra thick] (B) circle[radius=2];
    \draw[ultra thick] (C) circle[radius=2];
    \draw[ultra thick,rounded corners] (S-SE) rectangle (S-NW);
\end{tikzpicture}
}

\makeatother

\newcommand{\allvendiagrams}{
% To generate the lot:
\foreach \j in {0,...,255} {
  \def\venncode{}
  \foreach \k in {0,...,7} {
    \pgfmathparse{Mod(floor(\j/2^\k),2) == 1 ? "\venncode1" : "\venncode0"}
    \global\let\venncode=\pgfmathresult
  }
  \venn{\venncode}

}
}

\begin{document}
\venn{10000000}
\venn{01000000}
\venn{11000000}
\end{document}

After I changed documentclass to article, I got this error

! Package PGF Math Error: Unknown function `Mod'

I thought may be my version of PGF is old and therefore missing the Mod function. Did sudo apt-get update to get all missing updates, but the error did not go away. Do I need to upgrade the level of Ubuntu to a higher to get around this message?

After replacing all instances of Mod with mod in above code, I got this error

   ! Illegal unit of measure (pt inserted).
   <to be read again> 
                   ?
   l.57 \venn{10000000}

I need help in getting around this error and getting this sample run successfully.

The \venn command accepts a 8 digit binary string, goes over each digit and if a digit is 1, fills certain areas. For example, calling \venn{00000010} will fill everything in circle A which does not overlap with circle B or C.

Code relevant to iteration of binary string:

\def\venn@strip#1#2\venn@STOP{%
  \def\venn@next{#1}%
  \gdef\venn@rest{#2}%
}  

\edef\venn@rest{#100000000}%

\foreach \i in {0,...,7} {

  \begin{scope}[even odd rule]
    \expandafter\venn@strip\venn@rest\venn@STOP
    \ifnum\venn@next=1\relax
..............

I went through basic info about \def statement at http://en.wikibooks.org/wiki/TeX/def and confused about the \venn@STOP in \venn@strip after end of second argument and start of macro contents. All examples, I have seen have macro name followed by any optional arguments, immediately followed by contents of macro.

In a call of \venn{00000010}, before start of the 'for' loop, \venn@rest will be assigned 0000001000000000. In the 1st iteration \venn@next will be assigned 0000001000000000(same as what was passed into first argument) and \venn@rest will be assigned ""(nothing), same as what was passed into second argument to \venn@strip which was \venn@STOP(which has not been defined at this point). The \ifnum statement expects in \venn@next either a 0 or 1 for the position in the binary string corresponding to current iteration. I don't see where in the code such stripping of digits is happening.

The \relax directive asks TeX to do nothing. Not sure why we need \relax as part of \ifnum statement.

On a call of \venn{00000010}, statements related to filling are

  \begin{scope}[even odd rule]
\path[clip] (S-SE) rectangle (S-NW) (A) circle[radius=2];
\path[clip] (B) circle[radius=2];
\path[clip] (C) circle[radius=2];
\fill[rounded corners,red] (S-SE) rectangle (S-NW);
  \end{scope}

In iteration 2 of the for loop, \i = 1 and this is the only iteration which fills anything in the call.

Based on what is mentioned in the PGF manual, a clipping path restricts painting to a certain area. Also, multiple clippings accumulate and clipping is always done against the intersection of all clipping areas that have been specified inside the current scope.

The first clip statement, gives the circle at A as the clipping area. The second clip statement, gives the intersection of circle at A and B as the clipping area. The third third clip statement gives the intersection region of circles A, B and C as the clipping area. At the end when the rectangle is being filled, I expect the clipped area to be filled. Instead, what is filled is the area in circle A which is not overlapping with circle B or C.

The even odd rule probably makes the fill to work the way it does. This rule is explained in the manual in terms of shooting rays and count how often we hit the path. I could not understand how to use it.

6
  • 2
    You'll need to update your PGF version. The current version is 2.1.
    – Jake
    Sep 10, 2012 at 13:51
  • You can't get the latest version of TeX (or PGF) by running the system package manager. You'll need to install TeX Live (or just PGF) manually. See tex.stackexchange.com/q/1092/86 Sep 10, 2012 at 14:22
  • 1
    and tex.stackexchange.com/q/2044/86 for just PGF Sep 10, 2012 at 14:23
  • 1
    As for seeing the flow, it depends a bit on what parts are opaque. If you could give a bit more detail on that, I expect you'll get better answers. Sep 10, 2012 at 14:26
  • Based on link Andrew posted, I was able to install TexLive 2012 and now able to run the program successfully. I have edited my original post with questions where I need help in understanding the sample program.
    – Ravi
    Sep 13, 2012 at 18:57

1 Answer 1

3

Let me start with \venn@strip.

\def\venn@strip#1#2\venn@STOP{%
  \def\venn@next{#1}%
  \gdef\venn@rest{#2}%
}  

Here, we have a bit of slightly more complicated macro parameter specification. The macro parameter specification is everything up to the opening brace of the definition. In this case, it is #1#2\venn@STOP. So when TeX encounters \venn@strip then it will do its best to match that pattern. You can read more about exactly what it does in things like TeX by Topic, but let me try to explain what TeX does in this case. The pattern #1#2\venn@STOP says: grab everything up to the next occurrence of the token \venn@STOP, putting it in the parameters #1 and #2 according to these rules: if there is nothing, don't put anything in; if there is only one thing, put it in #1; if there is more, put one thing in #1 and the rest in #2. Moreover, the \venn@STOP is not examined and is thrown away, so its definition is immaterial. So the idea is that we want to evaluate \venn@strip 01011011010100001010\venn@STOP. It will then grab the first 0 as #1, the 1011011010100001010 as #2, and throw away the \venn@STOP.

The point is that we don't know how much stuff is in the rest of the string that we want to take the first character of, so we use a delimited macro to ensure that we get the rest.

Now, to its invocation:

\expandafter\venn@strip\venn@rest\venn@STOP

That \expandafter is key here. This reaches over the \venn@strip and expands \venn@rest before \venn@strip is considered. So when \venn@strip starts its stuff, the stream is actually \venn@strip010101101010100\venn@STOP and it can strip off the first character for testing.

(The extra zeros are pretty pointless. They're to guard against the user not putting in an 8-digit code.)

Next bit:

\ifnum\venn@next=1\relax

\ifnum is greedy. For each of its pieces (in this case, before and after the =) it will try to grab as much of a number as it can. It might not stop at the 1 if the next thing coming could potentially be a number. To guard against that (as I'm too lazy to figure out what the next thing is), the \relax is there. Essentially, \relax is playing the role of a "No numbers here" sign.

Last bit:

\path[clip] (S-SE) rectangle (S-NW) (A) circle[radius=2];

This is where the even odd rule comes in. This path is actually a rectangle with a circle inside it. Under the even odd rule, the region viewed as "inside" this path is the bit between the rectangle and circle. Inside the circle, we cross the path twice to "escape to infinity" - once being the circle and twice being the rectangle. Thus the clip is for the outside of the circle centred at (A), which corresponds to the complement of that circle. Neat, huh? (That bit was inspired by the reverse clip question somewhere on this site.)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .