# Creating complex diagrams

I need some help devising a method for further cases. I've searced tex.stackexchange.com for a solution but have so far not seen examples that help me much.

In writing my thesis, on musicology, I needed to create analytic diagrams, or networks if you will: see bottom picture. I've seen such diagrams made in LaTeX, but never one that hasn't involved way too many lines of code to be practical at any level except publishing - not to mention a degree in coding. So far i've been using Apple's Keynote for this and most of the time it is OK, especially the way it handles connecting lines to objects (like circles, adding arrows and manipulating the line.

But it is not an easy way to create networks larger than pentagons; everything from hexagons and up you have to create manually from a circle, that you divide into "cakes" corresponding to the number of corners you need, then connect the lines, delete the "cakes" and finaly attach the circles. Then to add to the insult, exporting the images is a pain: to keep things organised I need to create one Keynote file for each music example, ergo it contains several diagrams. The best way I've found to export this is to screenshot (!) the diagram in question then add it to my tex folder. The result cannot become 100% perfect whatever I do, and you might imagine my OCD sense tingling quite a bit...

My reasoning is thus: If I have to spend lots of time creating this manually anyway, why not make a TeX solution? I'm most likely heading for a PhD, and need this tool for many things and I think it would be a great tool for other musicologists.

To start off with the bare essentials: Is there some package that already provides the functionality needed to make an octatonic network as shown at the bottom?

I have quite a few ideas on what the language should look like for this to be an effective tool, but beggars can't be choosers, so I'll wait and see what you guys say first.

• Preparing graphics with LaTeX (my favorite package is TikZ) is indeed non-trivial and has a steep learning curve. It pays off, though. If you expect to draw such diagram often in the forseeable future, learning how to program them is definitely worth your time (imho). This site is an awesome resource to accompany your progress. – Raphael May 28 '15 at 12:36
• Just an aside: Keynote surely exports to .pdf (or .svg). If not I suggest it's unsuitable; although you can use a .pdf "printer" you often end up with low-res raster graphics. Either filetype can be modified (including from the command line) by inkscape; for .pdfs (which is what you'd include in your output) there are additional tools. – Chris H May 28 '15 at 12:56

You could try Tikz. It is pretty easy to position the elements correctly by specifying the angle and the distance. The code shows a straight-forward implementation of your screenshot.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,arrows,backgrounds}

\begin{document}
% Style definition for the large circles. The minimum width sets the minimum diameter of the circles
\tikzstyle{t1}=[circle,draw=gray,line width=2pt,fill=white,minimum width=1.2cm]
% Style definition for the small circles. Similar to the large ones, but they have a dashed border and a smaller font.
\tikzstyle{t2}=[dashed,circle,draw=gray,line width=2pt,fill=white,minimum width=.6cm,font=\small\sffamily]

\begin{tikzpicture}[font=\sffamily]
% The node in the center positioned at (0,0) with the name center and the label '$\mathsf{O_1}$'.
\node[font=\Huge\sffamily] at (0,0) (center) {$\mathsf{O_1}$};
% First node in the ring with the style 't1'. It is positioned relative to the node 'center' with a distance of 3cm and an angle  of 0°.
\node[t1] at ($(center) + (0:3cm)$) (F) {$\mathsf{F}$};
\node[t1] at ($(center) + (45:3cm)$) (Dm) {$\mathsf{Dm}$};
\node[t1] at ($(center) + (90:3cm)$) (D) {$\mathsf{D}$};
\node[t1] at ($(center) + (135:3cm)$) (Bm) {$\mathsf{Bm}$};
\node[t1] at ($(center) + (180:3cm)$) (B) {$\mathsf{B}$};
\node[t1] at ($(center) + (225:3cm)$) (Abm) {$\mathsf{A^bm}$};
\node[t1] at ($(center) + (270:3cm)$) (Ab) {$\mathsf{A^b}$};
\node[t1] at ($(center) + (315:3cm)$) (Fm) {$\mathsf{Fm}$};
\node[t1] at ($(Ab) + (0,-2.5cm)$) (G) {$\mathsf{G}$};
\node[t1,minimum width=0] at ($(G) + (2,.25cm)$) (Ebm) {$\mathsf{E^bm}$};

% The connections between the nodes in the ring.
\draw[line width=2pt,black] (D) -- (Dm) -- (F) -- (Fm) -- (Ab) -- (Abm) -- (B) -- (Bm) -- (D);
% The arrow from 'D' to 'B'. '-triangle 45' sets the arrow tip to a triangle with an angle of 45°. 'to[bend left]' says, that the arrow should bend left.
\draw[line width=2pt,-triangle 45] (D) to[bend left] (B);
% This one is a double headed arrow, so 'triangle 45-triangle 45'
\draw[line width=2pt,triangle 45-triangle 45] (B) to[bend left] (Ab);
\draw[line width=2pt,triangle 45-triangle 45] (D) to[bend left] (Ab);
\draw[line width=2pt,triangle 45-triangle 45] (Ab) to (G);
% The arrow from 'Ab' to 'Ebm'. It's dashed, double headed and smaler than the other ones.
% 'to[out=290,in=150]' sets the angles at the startpoints of the arrow. I used this, because 'bend left' didn't fit.
% The node command is used add a label to the arrow. 'xshift' moves the label by 4pt to the right.
\draw[dashed,line width=1pt,triangle 45-triangle 45] (Ab) to[out=290,in=150] node[above,font=\bf\sffamily,xshift=4pt] {M} (Ebm);

% The '/G' nodes on top. They are defined here, because they sould be on top of everything. You could also use layers but that's not necessary
\node[t2] at ($(D) + (0cm,.7cm)$) (G1) {/G};
\node[t2] at ($(B) + (0cm,.7cm)$) (G2) {/G};
\node[t2] at ($(Ab) + (.7cm,0cm)$) (G3) {/G};

\end{tikzpicture}
\end{document}

• This is closer to what a TikZ novice would write, and thus easier to digest (I guess). – Raphael May 28 '15 at 12:38
• Yes, I tried to make it as simple as possible without changing the result too much. – Daniel Schlegel May 28 '15 at 13:01
• I think this is wonderfull! Thank you all for sharing. It is still a bit overwhelming, though. Some parts of the code I understand what do, others not so much. Would you mind commenting a bit for each part? – Buschmann May 29 '15 at 17:09
• I added some comments. I hope they are enough. – Daniel Schlegel May 30 '15 at 7:58

Instead of drawing and placing each node, you can build the octagon shape by using Tikz trees, drawing the children in a clockwise (or counterclockwise if you prefer) fashion.

Then you set the angle of each child (360 degrees / 8 children = 45 degrees in this case), and the distance from the parent. The other nodes are build separately.

The only thing I couldn't fix is the weight of the font which looks bold.

## Code

\documentclass[tikz,margin=10pt]{standalone}
\usepackage{fixltx2e} % needed for \subscript{}

\usetikzlibrary{shapes, trees, arrows.meta, calc,positioning,backgrounds}

\tikzset{
every node/.style={circle, fill=white, line width=1.5pt, draw=gray, minimum size=1.2cm},
small/.style={dashed, minimum size=5mm},
alpath/.style={rectangle,draw=none, minimum size=5mm,fill=none},
couple/.style args={[#1]#2}{
label={[circle, fill=white, line width=1.5pt, draw=gray, dashed, minimum size=5mm,label distance=-10pt,#1]#2}},
level 1/.style={sibling angle=45, level distance=2.5cm},
edge from parent/.style= {draw=none},
}

\begin{document}
\begin{tikzpicture}[<->,{Latex}-{Latex}]\sffamily

\node[draw=none,font=\Huge] (main) at (0,0) {O\textsubscript{1}} [clockwise from=90]
child { node[couple={[small]90:/G}] (1) {D}}
child { node (2) {Dm}}
child { node (3) {F}}
child { node (4) {Fm}}
child { node[couple={[small]0:/G}] (5) {A\textsuperscript{b}}}
child { node (6) {A\textsuperscript{b}m}}
child { node[couple={[small]90:/G}] (7) {B}}
child { node (8) {Bm}}
;
\node[below =1cm of 5] (9) {G};
\node[right =1cm of 9] (10) {E\textsuperscript{b}m};

% Edges
\begin{scope}[on background layer]
\draw[ultra thick,-] (1) -- (2) -- (3) -- (4) -- (5) -- (6) -- (7) -- (8) -- (1);
\end{scope}

\draw[ultra thick,{Latex}-{Latex}] (1) edge[bend left] (5);
\draw[ultra thick,{Latex}-{Latex}] (5) edge[bend right] (7);
\draw[ultra thick,{Latex}-{Latex}] (7) edge[bend right] (1);
\draw[ultra thick] (5) -- (9);
\draw[-,dashed,thick] (10.north west) edge[bend left=10,<->,{Latex}-{Latex}] node[alpath,midway,anchor=west] {M} (5.south east) ;
\end{tikzpicture}
\end{document}

• Font weights can probably be fixed by choosing another font? – Raphael May 28 '15 at 12:37
• @Raphael I was looking into it, but it's more complicated than it's worth. :D Or at least it seemed like that. – Alenanno May 28 '15 at 12:48
• @Alenanno Cool answer, one question is what's the reason to use background layer? – Beatlej Jun 6 '15 at 2:28
• @Beatlej Try commenting out \begin{scope}[on background layer] and \end{scope then compile it. :D In Tikz what is defined later is above anything done previously. So that edge would be all over the nodes (not what we want). Using the background layer they are typeset below the nodes. – Alenanno Jun 6 '15 at 8:59

A solution with the pst-poly package:

\documentclass[pdf, x11names]{standalone}%

\usepackage [default]{cabin}
\usepackage{changes}
\usepackage{pst-poly}

\newcommand\circleG{\circlenode[ linecolor=SlateGray4!50!, fillstyle=solid, fillcolor=white, ]{}{/G}}

\begin{document}

\psset{unit=2.8cm, radius=16pt, linecolor=SlateGray4!50!, linewidth=1.5pt, framesep=4pt, arrowinset=0, arrowscale=1.5}
\fbox{\begin{pspicture*}(-2,-2)(2,3)
\providecommand{\PstPolygonNode}{%
\Cnode[linecolor=SlateGray4!50!, fillstyle=solid, fillcolor=white](1;\INode ){N}
}
\rput{22.5}(0,0) {\PstOctogon[PolyName=A, linecolor=black]}
\rput(A1){Dm}\rput(A2){D}\rput(A3){Bm}\rput(A4){B}\rput(A5){A\boldmath$^{^{\scriptstyle\flat}}\mkern-7mu$m}\rput(A6){A\rlap{\boldmath$^\flat$}} \rput(A7){Fm}\rput(A8){F}
\nput[labelsep=45pt]{-90}{A6}{\circlenode[framesep=9.6pt]{G}{G}}
\nput[labelsep=28pt]{0}{G}{\circlenode[framesep=2pt]{B}{B\boldmath$^{^{\scriptstyle\flat}}\mkern-7mu$m}}
\psset{labelsep=8pt, linestyle=dashed, dash=3pt 4pt}
\nput{90}{A2}{\circleG}\nput{180}{A4}{\circleG}\nput{0}{A6}{\circleG}
\rput(A0){\LARGE O\textsubscript{1}}
%%%Node connections
\psset{arrows= <->, linestyle=solid, linecolor=black, nodesep=16pt, arcangle=30}
\ncarc{A2}{A6}\ncarc[arrows=->]{A2}{A4}\ncarc{A4}{A6}
\ncline[nodesepB=0pt]{A6}{G}\ncarc[linewidth=1pt, linestyle=dashed, arcangle=10, nodesepA=0pt]{B}{A6}\nbput[labelsep=2pt]{M}
\end{pspicture*}}

\end{document} %


It is also possible to use external programs which have their own full-fledged programming language and have been devised as graphic companions of (La)TeX, such as Asymptote or MetaPost. Here is an attempt with MetaPost, using the boxes package. I have inserted it in a LuaLaTeX program since MetaPost has been integrated into LuaTeX, but it could have been made independently.

\documentclass[border=2mm]{standalone}
\usepackage{fixltx2e}
\usepackage{luamplib}
\mplibtextextlabel{enable}
\mplibsetformat{metafun}
\renewcommand{\familydefault}{\sfdefault}
\begin{document}
\begin{mplibcode}
input boxes
circmargin := 6bp;
def between(suffix a, b) =
cutbefore bpath a blownup 1.5bp cutafter bpath b blownup 1.5bp
enddef;

def clearedbox(suffix d) =
unfill bpath d; drawunboxed(d);
draw bpath d withcolor .5white;
enddef;

def dashed_clearedbox(suffix d) =
unfill bpath d; drawunboxed(d);
draw bpath d dashed evenly scaled 0.8 withcolor .5white;
enddef;

beginfig(1);
pickup pencircle scaled 1.5bp;
u := 3cm; i := 0;
for st="F","Dm","D","Bm","B","A\textsuperscript{b}m", "A\textsuperscript{b}", "Fm":
circleit.c[i](st); c[i].c = u*dir(45i);
i := i + 1;
endfor

label("\Huge O\textsubscript{1}", origin);
for i = 1 upto 8:
j := i mod 8;
draw c[i-1].c -- c[j].c;
endfor
for i = 0 upto 7: clearedbox(c[i]);endfor;

circleit.e6("G"); e6.c = c6.c + (0, -1.5cm); clearedbox(e6);
circmargin := 2bp;
circleit.f6("\scriptsize E\textsuperscript{b}m");
f6.c = c6.c + (1.5cm, -1.5cm); clearedbox(f6);

drawdblarrow c2.c{dir -60} .. c6.c{dir -120} between (c2, c6);
drawdblarrow c2.c{dir -120} .. c4.c{dir -150} between (c2, c4);
drawdblarrow c4.c{dir -30} .. c6.c{dir -60} between (c4, c6);
drawdblarrow c6.c -- e6.c between (c6, e6);

circmargin := 2bp;
for i = 2, 4, 6:
circleit.d[i]("\texttt{\slash}G"); d[i].c = c[i].c +
if i<>6: (0, 5.5mm) else: (6mm, 0) fi; dashed_clearedbox(d[i]);
endfor

path dline; dline = c6.c{dir -70} .. f6.c{dir -20};
drawdblarrow dline cutbefore bpath c6 blownup 1.25bp
cutafter bpath f6 blownup 1.25bp
withpen pencircle dashed evenly;
label.urt("M", point 0.55 along dline);
endfig;
\end{mplibcode}
\end{document}


• You can also include it directly in pdflatex using the gmp package. – dgoodmaniii May 30 '15 at 19:39
• @dgoodmanii Yes, I know this great package. Only it needs the shell-escape feature, or else a triple compilation (LaTeX+mpost+LaTeX). In both cases it makes the typesetting of the (pdf)LaTeX file much slower. This is the main reason for which I use luamplib (and also the main reason for which I have switched to LuaLaTeX, by the way :-)). – Franck Pastor May 30 '15 at 19:54

Here's another TikZ solution. It uses regular polygon from the shapes.geometric library. The library positioning is used for the relative position of the other nodes.

\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{shapes.geometric}
\usetikzlibrary{positioning}

\tikzstyle{circles}  = [circle,draw=gray,line width=2pt,minimum width=1.2cm]
\tikzstyle{dcircles} = [circle,draw=gray,line width=2pt,minimum width=0.6cm,fill=white,dashed]

\begin{document}
\begin{tikzpicture}[font=\sffamily,>=latex]
% Polygon
\node[regular polygon, regular polygon sides=8, minimum width=6cm, rotate=22.5] (O) {}  (O.center) node[font=\Huge\sffamily] (cntr) {O$_\mathsf{1}$};

% Polygon vertices
\foreach \n/\c [count=\i] in {D/D,Bm/Bm,B/B,Abm/A$^\mathsf{b}$m,Ab/A$^\mathsf{b}$,Fm/Fm,F/F,Dm/Dm}
\node[circles] at (O.corner \i)  (\n) {\c};
% Other vertices
\node[circles,below=1cm of Ab] (G) {G};
\node[circles,above right=-.6cm and 1cm of G] (Ebm) {E$^\mathsf{b}$m};

% Connect polygon vertices
\draw[line width=2pt,black] (D) -- (Dm) -- (F) -- (Fm) -- (Ab) -- (Abm) -- (B) -- (Bm) -- (D);
% Draw arrows
\foreach \f/\t/\l/\b in {D/B/->/bend left, B/Ab/<->/bend left, D/Ab/<->/bend left, Ab/G/<->/}
\draw[line width=2pt,\l] (\f) to[\b] (\t);
\draw[dashed,line width=1pt,<->] (Ab) to[out=290,in=150] node[above,xshift=4pt] {M} (Ebm);