28

I can draw Venn diagrams but now my question is more complicated than just drawing Venn diagrams one by one. I need to draw all possible Venn diagrams efficiently. I need a combinatorical approach because there are 256 combinations.

Let's choose the following sets as the case to consider. The binary number labels are used to uniquely identified each "atomic" region. My definition: An atomic region does not contain any smaller region.

Because there are 8 atomic regions, each can be either selected or not to compose a new compound region. Therefore there are 2^8 ways.

How to generate all possible Venn diagrams (with the case above) efficiently?

enter image description here

The objective is to produce 256 Venn diagrams, each diagram has a unique colored compound region that has an associated set operation.

Let's use 8-bit integer to represent each diagram.

The first bit (the most left bit or the most significant bit) represents the region 000.

The second bit represents the region 100.

...

The least significant bit represents the region 111.

Bit 0 represent not-join to produce a new compound region. And 1 otherwise.

If the first diagram with the complement of AuBuC then its binary representation is 1000 0000. 1111 1111 represents S. Etc etc etc!

The Problem Sheet

The problem sheet will ask the student to find a set operation (not necessarily unique) for each RED COMPOUND region in each diagram below.

enter image description here enter image description here enter image description here

...

4
  • Have you look at this? There are some examples... it shouldn't be hard to generalise to the other cases. Cheers.
    – Dox
    Commented Aug 16, 2012 at 13:19
  • If I understand the question, you should create the diagram as shown above, with 8 separate \fill commands to shade each of the 8 individual regions. Each of the individual \fill commands needs to be surrounded by an if conditional that selects whether the given region is shaded. The if conditional should only shade the region if the given region's number is in a list. Then you can either provide a list of regions to shade, or generate all possible combinations of the list. Commented Aug 16, 2012 at 14:34
  • I asked the 2-variable version of this question here: tex.stackexchange.com/questions/266326/… Commented Sep 7, 2015 at 19:16
  • if you just want the images I have uploaded them all here: github.com/Ace-Cassidy/Venn-Diagram-Pictures
    – Ace.C
    Commented Jun 2, 2020 at 21:20

4 Answers 4

27

Right, here's some code:

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

And here's the result:

Sample venn diagrams

I almost certainly have used a different code for the different regions - I went for simpler code. The rubric is that a 1 in the kth place fills the kth region, and the correspondence between labels and regions is to write out k as a binary number, then if the bit is set, that circle is used inside and if not, outside. At the end, we draw the region and circles on top. I haven't gone for much customisability, but hopefully it's fairly obvious what to change to get it to look different.

Edit from the questioner: By using \documentclass[border=3pt,tikz]{standalone} and invoking \allvendiagrams instead of \venn{10000000}\venn{01000000}\venn{11000000}, the output will show all the Venn diagrams as follows. But not in GIF for sure.

enter image description here

5
  • @GarbageCollector (I don't know if you'll get notified by this comment ...) Nice animation! Care to edit in the code to make it animate? I don't know how to do that and I'd like to learn. Commented Aug 16, 2012 at 17:13
  • You need ImageMagick installed on your machine. After creating a PDF file that contains at least two pages, you can animate them. Invoke convert -delay <integer (in 1/100 miliseconds)> -loop 0 -density <integer> -alpha <on,off,remove (choose remove to make the transparent opaque)> <inputfile>.pdf <outputfile>.gif Commented Aug 16, 2012 at 17:19
  • For example: convert -delay 50 -loop 0 -density 15 -alpha remove venn.pdf venn.gif will generate a GIF with 100/50 = 2 frames per second. For more details, see the last update in this link. Commented Aug 16, 2012 at 17:21
  • 2
    @GarbageCollector Thank you! Now I've learnt something ... useful. Commented Aug 16, 2012 at 17:24
  • Could you make this work for 2-variable Venn diagrams? (See follow-up question:) tex.stackexchange.com/questions/266326/… Commented Sep 7, 2015 at 20:43
33

This could be done more elegantly, but it works nonetheless. Based on the answer to Is it possible to fill the complement of (A union B union C) with a solid color but the remaining regions remain transparent?

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}
\tikzstyle{reverseclip}=[insert path={(current page.north east) --
  (current page.south east) --
  (current page.south west) --
  (current page.north west) --
  (current page.north east)}
]

\tikzset{
    venn0/.code={
        \begin{scope}
            \begin{pgfinterruptboundingbox}
            \path  [clip] (-90:0.7cm) circle [radius=1cm] [reverseclip];
            \path  [clip] (30:0.7cm) circle [radius=1cm] [reverseclip];
            \path  [clip] (-210:0.7cm) circle [radius=1cm] [reverseclip];
            \end{pgfinterruptboundingbox}
            \fill [orange] (-2,-2) rectangle (2,2);
        \end{scope}
    },
    venn1/.code={
        \begin{scope}
            \begin{pgfinterruptboundingbox}
            \path  [clip] (-90:0.7cm) circle [radius=1cm];
            \path  [clip] (30:0.7cm) circle [radius=1cm] [reverseclip];
            \path  [clip] (-210:0.7cm) circle [radius=1cm] [reverseclip];
            \end{pgfinterruptboundingbox}
            \fill [orange] (-2,-2) rectangle (2,2);
        \end{scope}
    },
    venn2/.code={
        \begin{scope}
            \begin{pgfinterruptboundingbox}
            \path  [clip] (-90:0.7cm) circle [radius=1cm] [reverseclip];
            \path  [clip] (30:0.7cm) circle [radius=1cm];
            \path  [clip] (-210:0.7cm) circle [radius=1cm] [reverseclip];
            \end{pgfinterruptboundingbox}
            \fill [orange] (-2,-2) rectangle (2,2);
        \end{scope}
    },
    venn3/.code={
        \begin{scope}
            \begin{pgfinterruptboundingbox}
            \path  [clip] (-90:0.7cm) circle [radius=1cm] [reverseclip];
            \path  [clip] (30:0.7cm) circle [radius=1cm] [reverseclip];
            \path  [clip] (-210:0.7cm) circle [radius=1cm];
            \end{pgfinterruptboundingbox}
            \fill [orange] (-2,-2) rectangle (2,2);
        \end{scope}
    },
    venn4/.code={
        \begin{scope}
            \begin{pgfinterruptboundingbox}
            \path  [clip] (-90:0.7cm) circle [radius=1cm] [reverseclip];
            \path  [clip] (30:0.7cm) circle [radius=1cm] ;
            \path  [clip] (-210:0.7cm) circle [radius=1cm] ;
            \end{pgfinterruptboundingbox}
            \fill [orange] (-2,-2) rectangle (2,2);
        \end{scope}
    },
    venn5/.code={
        \begin{scope}
            \begin{pgfinterruptboundingbox}
            \path  [clip] (-90:0.7cm) circle [radius=1cm];
            \path  [clip] (30:0.7cm) circle [radius=1cm] [reverseclip];
            \path  [clip] (-210:0.7cm) circle [radius=1cm];
            \end{pgfinterruptboundingbox}
            \fill [orange] (-2,-2) rectangle (2,2);
        \end{scope}
    },
    venn6/.code={
        \begin{scope}
            \begin{pgfinterruptboundingbox}
            \path  [clip] (-90:0.7cm) circle [radius=1cm];
            \path  [clip] (30:0.7cm) circle [radius=1cm] ;
            \path  [clip] (-210:0.7cm) circle [radius=1cm] [reverseclip];
            \end{pgfinterruptboundingbox}
            \fill [orange] (-2,-2) rectangle (2,2);
        \end{scope}
    },
    venn7/.code={
        \begin{scope}
            \begin{pgfinterruptboundingbox}
            \path  [clip] (-90:0.7cm) circle [radius=1cm];
            \path  [clip] (30:0.7cm) circle [radius=1cm] ;
            \path  [clip] (-210:0.7cm) circle [radius=1cm];
            \end{pgfinterruptboundingbox}
            \fill [orange] (-2,-2) rectangle (2,2);
        \end{scope}
    },
    vennoutlines/.code={
        \draw (-90:0.7cm) circle [radius=1cm];
        \draw (30:0.7cm) circle [radius=1cm];
        \draw (-210:0.7cm) circle [radius=1cm];
    }
}

\noindent%
\foreach \a in {0,1}
\foreach \b in {0,1}
\foreach \c in {0,1}
\foreach \d in {0,1}
\foreach \e in {0,1}
\foreach \f in {0,1}
\foreach \g in {0,1}
\foreach \h in {0,1}{%
\begin{tikzpicture}[remember picture, scale=0.2]
\ifnum\a=0
    \tikzset{venn0}
\fi
\ifnum\b=0
    \tikzset{venn1}
\fi
\ifnum\c=0
    \tikzset{venn2}
\fi
\ifnum\d=0
    \tikzset{venn3}
\fi
\ifnum\e=0
    \tikzset{venn4}
\fi
\ifnum\f=0
    \tikzset{venn5}
\fi
\ifnum\g=0
    \tikzset{venn6}
\fi
\ifnum\h=0
    \tikzset{venn7}
\fi
\tikzset{vennoutlines}
\end{tikzpicture}
}

\end{document}
6
  • You well understood my question. But it is the most efficient algorithm? Commented Aug 16, 2012 at 15:01
  • 1
    @GarbageCollector: Define "efficient".
    – Jake
    Commented Aug 16, 2012 at 15:02
  • The most important one is the efficiency in the number of characters used in the code. Speed can be ignored. DRY (don't repeat yourself) philosophy says that the code repetition must be as minimal as possible. :-) Commented Aug 16, 2012 at 15:04
  • 1
    @GarbageCollector: It is most definitely not the most efficient algorithm then. It does, however, get the task done. Making it more efficient is left as an exercise for people smarter than me.
    – Jake
    Commented Aug 16, 2012 at 15:06
  • 1
    Since I, as mere observer, don't care about efficiency, I can say that I really like this answer. Commented Aug 16, 2012 at 15:30
16

Here's my solution. It's quite like Jake's solution, but with a few differences. I computed "nice" circle radii and center separations (2 and 2*sqrt{3}), thus I can use coordinates directly and avoid reverseclip, this shortens the definition of the regions. Furthermore, my foreach loops use colors as variables, that way you can avoid \ifnum \fi constructs. The final \phantom{X} is just to keep the last picture on the same line as the other 15. I don't know how fast Jake's code is, but mine is quite slow.

\documentclass[parskip]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}

\begin{document}

\newcommand{\regionA}[1]
{   \fill[#1] (30:2) arc (60:0:{2*sqrt(3)}) arc (-60:120:{2*sqrt(3)}) arc (60:0:{2*sqrt(3)});
}

\newcommand{\regionB}[1]
{   \fill[#1] (150:2) arc (180:120:{2*sqrt(3)}) arc (60:240:{2*sqrt(3)}) arc (180:120:{2*sqrt(3)});
}

\newcommand{\regionC}[1]
{   \fill[#1] (270:2) arc (240:300:{2*sqrt(3)}) arc (360:180:{2*sqrt(3)}) arc (240:300:{2*sqrt(3)});
}

\newcommand{\regionAB}[1]
{   \fill[#1] (30:2) arc (0:60:{2*sqrt(3)}) arc (120:180:{2*sqrt(3)}) arc (120:60:{2*sqrt(3)});
}

\newcommand{\regionAC}[1]
{   \fill[#1] (30:2) arc (60:0:{2*sqrt(3)}) arc (300:240:{2*sqrt(3)}) arc (-60:0:{2*sqrt(3)});
}

\newcommand{\regionBC}[1]
{   \fill[#1] (150:2) arc (120:180:{2*sqrt(3)}) arc (240:300:{2*sqrt(3)}) arc (240:180:{2*sqrt(3)});
}

\newcommand{\regionABC}[1]
{   \fill[#1] (30:2) arc (60:120:{2*sqrt(3)}) arc (180:240:{2*sqrt(3)}) arc (-60:0:{2*sqrt(3)});
}

\newcommand{\regionDarkside}[1]
{   \fill[#1,even odd rule] (90:4) arc (120:-60:{2*sqrt(3)}) arc (360:180:{2*sqrt(3)}) arc (240:60:{2*sqrt(3)}) (0,0) circle (7);
}

\newcommand{\mycolor}{blue!50!cyan}
\newcommand{\mynocolor}{white}

\foreach \a in {\mycolor,\mynocolor}
{   \foreach \b in {\mycolor,\mynocolor}
    { \foreach \c in {\mycolor,\mynocolor}
        {   \foreach \d in {\mycolor,\mynocolor}
            {   \foreach \e in {\mycolor,\mynocolor}
                { \foreach \f in {\mycolor,\mynocolor}
                    {   \foreach \g in {\mycolor,\mynocolor}
                        {   \foreach \h in {\mycolor,\mynocolor}
                            {   \begin{tikzpicture}[scale=0.05]
                                    \regionA{\a}
                                    \regionB{\b}
                                    \regionC{\c}
                                    \regionAB{\d}
                                    \regionAC{\e}
                                    \regionBC{\f}
                                    \regionABC{\g}
                                    \regionDarkside{\h}
                                    \draw (30:2) circle ({2*sqrt(3)});
                                    \draw (150:2) circle ({2*sqrt(3)});
                                    \draw (270:2) circle ({2*sqrt(3)});
                                    \draw (0,0) circle (7);
                                    \clip (0,0) circle (7);
                                    \useasboundingbox (0,0) circle (7);
                                \end{tikzpicture}
                            }
                        }
                    }
                }
            }
        }
    }
}
\phantom{X}

\end{document}

enter image description here

3
  • 1
    Thanks for answering. But according to Jeff Atwood, the arrow must be flattened. :-D Commented Aug 16, 2012 at 16:12
  • 1
    Apparently, I'm not operating a 1280x1024 display. I think I could have added another 40 levels without problems ;) Commented Aug 16, 2012 at 16:19
  • @GarbageCollector That's Jeff Atwood talking. You might not want to learn to socialize from Mark Zuckerberg.
    – percusse
    Commented Aug 16, 2012 at 16:29
10

Here's a metapost version (compile with lualatex). A portion of the output:

enter image description here

\documentclass{article}
\usepackage{luamplib}

\begin{document}
\begin{mplibcode}
    u:=10;
    path p[];
    picture r[];
    numeric k[];
    cmykcolor mycol;
    mycol:=(0.21,0.12,0,0.07);

    z1 = (u/4,-(u/4)/sqrt(3));
    p1 = fullcircle scaled u;
    p2 = p1 shifted (u/2,0);
    p3 = p1 shifted (u/4,-u/4*sqrt(3));
    p4 = unitsquare scaled 1.6u shifted (-u/2-.05*u,-u);

    p.1 = buildcycle(p1,p2,reverse(p3));
    p.2 = buildcycle(p1,reverse(p2),reverse(p3));
    p.3 = buildcycle(reverse(p1),reverse(p2),reverse(p3));

    r0:=image(draw p1; draw p2; draw p3; draw p4;);
    drawoptions(withcolor mycol);
    r1:=image(fill p4; unfill p1;unfill p2;unfill p3;);
    r2:=image(fill p.1);
    r3:=image(fill p.2);
    r4:=image(fill p.3);
    r5:=image(fill p.1 rotatedabout(z1,120));
    r6:=image(fill p.1 rotatedabout(z1,240));
    r7:=image(fill p.2 rotatedabout(z1,120));
    r8:=image(fill p.2 rotatedabout(z1,240));
    drawoptions();

    % this converts a number to binary
    vardef tobinary(expr n)=
      save m; m = n;
      j:= 1;
      forever: exitunless m>0;
        b:= m mod 2;
        m:= floor (m/2);
        k[j]:=b;
        j:=j+1;
      endfor;
    k[0]:= j-1;
    enddef;

    beginfig(1);
    for i = 0 upto 255:
      drawoptions(shifted (1.6*u*(i div 16), -1.6*u*(i mod 16)));
      tobinary(i);
      for j = 1 upto k[0]:
        if k[j]=1: draw r[j]; fi;
      endfor;
      draw r[0];
    endfor;
    endfig;

end;

\end{mplibcode}
\end{document}

You must log in to answer this question.

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