9

Can I draw something like this programmatically with LaTeX?

I drew these with Adobe Illustrator, but it gets pretty time-consuming.
Even with relatively few nodes. What it attempts to demonstrate is the number of "symmetric" cryptographic keys required for secure communication between n parties - for which the formula is n(n-1)/2 (I tried to render that with LaTeX/MathJax, but it won't let me for some reason).

enter image description here

And it's hard to get it perfect. It's always off by a small amount, as you can see here. It might not seem like much but with a lot of nodes, it can add up and skew the output, and cause problems when trying to get everything to fit properly.

enter image description here

I wanted to draw a big one with up up to 100 nodes, so it would be best if I could do it programmatically, rather than having to type up several hundred lines of code manually. Is that kind of thing possible with LaTeX? I heard someone say TeX is a full Turing complete programming language. If that's true, I'm not sure, but it would probably make it pretty useful for generating diagrams with this kind of data, kinda like D3.js.

As you can see, by the time you get to 10 or 11 vertices, it can be pretty unruly:

enter image description here

  • After all, 100 vertices is a lot. How are you going to arrange vertices? – Symbol 1 May 31 at 17:23
  • @Symbol1 Same as I have been, probably. Around. A circle or a polygon. Doesn't necessarily have to be 100 vertices, I want to be able to specify an arbitrary number, and produce a graph with that many interconnected nodes. In a similar fashion to what I've drawn. I experimented with your GitHub code and got it to do some cool things, but I was just hacking around, manipulating values, not really understanding a lot of it. – voices May 31 at 18:15
  • Consider accepting the provided answer since it seems to answer your question. – Dr. Manuel Kuehner May 31 at 18:28
  • @tjt263 Would you mind sharing in which context you're going to use it? – xxx--- May 31 at 18:29
  • The same is true for some of your other questions. – Dr. Manuel Kuehner May 31 at 18:30
11

Yes, you can.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{arrows.meta}
\newcounter{pft}
\begin{document}
\begin{tikzpicture}[font=\sffamily,pics/cgram/.style={code={
\foreach \XX [count=\YY starting from 0] in {1,...,#1}
{\pgfmathsetmacro{\mycolor}{{\LstCols}[\YY]}
\node[circle,draw,minimum size=2.5em,fill=\mycolor] (c-#1-\XX) at 
({{\LstAngles}[#1-2]-\YY*360/#1}:1.5) {\setcounter{pft}{\XX}\Alph{pft}};}
\foreach \XX [evaluate=\XX as \Ymax using {int(\XX-1)}] in {2,...,#1}
{\foreach \YY  in {1,...,\Ymax}
{\pgfmathsetmacro{\mycolorA}{{\LstCols}[\XX-1]}
\pgfmathsetmacro{\mycolorB}{{\LstCols}[\YY-1]}
\path (c-#1-\XX) -- (c-#1-\YY) coordinate[pos=0.1] (aux0) coordinate[pos=0.9] (aux1);
\fill[black] (aux0) to[bend left=2] (aux1) to[bend left=2] (aux0);
\draw[{Stealth[fill=\mycolorB,length=7pt,inset=2pt]}-{Stealth[fill=\mycolorA,length=7pt,inset=2pt]}] (c-#1-\XX) -- (c-#1-\YY);
}}}}]
\def\LstCols{"red","orange","yellow","green!70!black","blue!70!white","purple!80!white"}
\def\LstAngles{180,150,135,128,150}
\path (-5,0) pic {cgram=2} (0,0.5) pic {cgram=3} (5,0) pic {cgram=4}
 (-3,-4) pic {cgram=5}  (3,-4) pic {cgram=6};
\end{tikzpicture}
\end{document}

enter image description here

Zoom in:

enter image description here

And yes, for large numbers N of nodes it becomes busy, simply since the number of connections goes like N (N-1)/2.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{arrows.meta}
\definecolor{colorA}{RGB}{202, 38, 49} 
\definecolor{colorB}{RGB}{222, 146, 60} 
\definecolor{colorC}{RGB}{240, 215, 68} 
\definecolor{colorD}{RGB}{126, 183, 86} 
\definecolor{colorE}{RGB}{98, 173, 233} 
\definecolor{colorF}{RGB}{158, 76, 150}  
\newcounter{pft}
\tikzset{pics/cgram/.style={code={
\foreach \XX [count=\YY starting from 0] in {1,...,#1}
{\pgfmathtruncatemacro{\iA}{mod(\XX-1,6)+1}
\pgfmathsetmacro{\mycolor}{{\LstCols}[\iA-1]}
\node[circle,draw,minimum size=2.5em,fill=\mycolor] (c-#1-\XX) at 
({-\YY*360/#1}:\pgfkeysvalueof{/tikz/cgram radius}) {\setcounter{pft}{\iA}\Alph{pft}};}
\foreach \XX [evaluate=\XX as \Ymax using {int(\XX-1)}] in {2,...,#1}
{\foreach \YY  in {1,...,\Ymax}
 {\pgfmathtruncatemacro{\iA}{mod(\XX-1,6)+1}
  \pgfmathtruncatemacro{\iB}{mod(\YY-1,6)+1}
  \pgfmathsetmacro{\mycolorA}{{\LstCols}[\iA-1]}
  \pgfmathsetmacro{\mycolorB}{{\LstCols}[\iB-1]}
 \draw[{Stealth[fill=\mycolorB,length=7pt,inset=2pt]}-{Stealth[fill=\mycolorA,length=7pt,inset=2pt]}] (c-#1-\XX) -- (c-#1-\YY);
}}
}},cgram radius/.initial=1.5}
\begin{document}
\foreach \Nmax in {2,4,...,40}
{\begin{tikzpicture}[font=\sffamily]
\draw (-11,-11) rectangle (11,11);
\def\LstCols{"colorA","colorB","colorC","colorD","colorE","colorF"}
\pgfmathsetmacro{\myradius}{sqrt(2.5*\Nmax)}
\path  pic[cgram radius=\myradius] {cgram=\Nmax};
\end{tikzpicture}}
\end{document}

enter image description here

  • 1
    @marmot What I meant was this: in my construction, I can avoid \LstAngles{180,150,135,128,150} because TikZ will choose the right angle automatically. – Symbol 1 May 31 at 7:34
  • 2
    +1: Surprisingly little code needed for that. – Dr. Manuel Kuehner May 31 at 10:29
  • 1
    @marmot Can you please explain your code a bit? – voices May 31 at 15:07
  • 1
    @tjt263 You might be interested in github.com/pgf-tikz/pgf/issues/640#issuecomment-504888430. I am working on this, but did not accomplish much so far since I have a problem that parallels yours: I do not understand how to deal with github. So I think I know how you feel. On the other hand, I feel that the situations are different in that I usually add explanations if I get asked on something specific. However, as of now I have not yet received answers to my github questions that allow me to get going. This is mostly my fault, but also the fact that I did not find a manual on this. – user121799 Jul 8 at 17:30
  • 1
    I have cleaned up some comments here: they don't really help to refine the answer (which ultimately is the aim of comment threads). – Joseph Wright Aug 6 at 9:00
4

So this is my construction for future references.

\documentclass[border=9,tikz,rgb]{standalone}

\usetikzlibrary{arrows.meta,decorations.pathreplacing}
\begin{document}

\tikzset{
    /pgf/arrow keys/colorsize/.style={fill=#1,length=10pt}
}
\def\N{70}
\tikzdeclarecoordinatesystem{sunflower}{ % #1 is the index of vertex
    \pgfmathsetmacro\sunindex{#1-.5}
    \pgfmathsetmacro\sunangle{mod(\sunindex*16.18034,10)*36}
    \pgfmathsetmacro\sunradius{sqrt(\sunindex)*50}
    \pgfpointpolar{\sunangle}{\sunradius}
}
\globalcolorstrue
\def\definesuncolor#1{
    \pgfmathtruncatemacro\sunindex{#1-.5}
    \pgfmathsetmacro\sunhue{mod(\sunindex*16.18034,10)*36}
    \pgfmathsetmacro\sunsaturation{sqrt(\sunindex/\N)}
    \definecolor{sun#1}{Hsb}{\sunhue,\sunsaturation,1}
}
\tikz{
    \foreach\i in{1,...,\N}{
        \definesuncolor{\i}
        \path(sunflower cs:\i)node(vertex\i)
            [circle,draw,minimum size=2cm,line width=6pt]{};
        \fill[sun\i](vertex\i)+(1pt,1pt)circle(1);
    }
    \foreach\i in{2,...,\N}{
        \foreach\j in{1,...,\numexpr\i-1}{
            \path[scale=.666/sqrt(\N)]
                [shift=(vertex\i)](sunflower cs:\j)coordinate(X-\i-\j)
                [shift=(vertex\j)](sunflower cs:\i)coordinate(Y-\i-\j);
            \draw[{Stealth[colorsize=sun\j]}-{Stealth[colorsize=sun\i]}]
                [line width=.1](X-\i-\j)--(Y-\i-\j);
        }
    }
    \foreach\i in{2,...,\N}{
        \foreach\j in{1,...,\numexpr\i-1}{
            \draw[{Stealth[colorsize=sun\j]}-{Stealth[colorsize=sun\i]}]
                [dash pattern=on0off9999](X-\i-\j)--(Y-\i-\j);
        }
    }
}

\end{document}

Some comments to whomever wants to play with this:

  • sunflower is the coordinate system that controls how to place vertices. It is the same algorithm that sunflower uses to place its seeds. See wikipedia
  • The color of each vertex is control by \definesuncolor#1. Currently it is defined such that the sunflower looks like the HSB wheel.
  • There are two nested-for-loops at the end. The former loop draws the edge, the later loop draws the arrow tips.
  • The position of arrow tip is controlled by (X-\i-\j) and (Y-\i-\j). Currently they are the relative positions of the vertices. So the arrows tips on each vertex also looks like the HSB wheel.

  • Oh, my, that is one beautiful mess! – Phelype Oleinik May 31 at 19:46
  • I doubt you need anchors here. ;-) – user121799 May 31 at 20:27
  • @marmot Good question. If I work hard enough, I should be able to define an anchorborder that works. – Symbol 1 May 31 at 21:02
  • If anyone's trying to count, this is 70 vertices. – voices Jun 9 at 4:25
  • Was able to do 100 nodes with LuaLaTeX. – voices Jun 9 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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