# How can I draw nomograms in LaTeX?

## What is a nomogram

“A nomogram, also called a nomograph, alignment chart or abaque, is a graphical calculating device, a two-dimensional diagram designed to allow the approximate graphical computation of a function.”

## Example

The rocket equation, given at the top of this nomogram, can be solved by drawing a line from the scale on the left to the scale on the right. The solution can be read off from the scale in the middle.

## Question

How can I reproduce the figure above in LaTeX? Is there a TikZ approach?

## Bonus question

How can I create arbitrary nomograms with LaTeX?

The figure above has been created with: http://pynomo.org/wiki/index.php?title=Main_Page

-
So you saw my comment on that post from r/kerbalspaceprogram, uh? :P – JorgeGT May 13 '14 at 19:53
@JorgeGT yes indeed :P. Immediately searched the web for a TikZ solution but could not find one. – Ingo May 14 '14 at 8:33
@Ingo I completed my answer with more details and possibilities. – Tarass May 16 '14 at 23:07

### Disclaimer

Take this as a starting point: there are a few things to be done to make the solution really working in all cases. Namely:

• handle cases where remainder(max/min)!=0
• handle min!=0
• handle scale from max to min besides min to max

A possibility:

\documentclass[tikz,border=10pt]{standalone}
\usepackage{pdftexcmds,etoolbox}

\makeatletter
\newif\ifroundedenabled%
\newif\ifscalemaxtomin%
\newif\ifscalefromzero%
\pgfkeys{/nomogram/.cd,
% keys for a single diagram
part 1/.style={},
part 2/.style={},
part 3/.style={},
single diagram/.cd,
scale max to min/.is if=scalemaxtomin,
scale max to min=false,
scale from zero/.is if=scalefromzero,
scale from zero=true,
at pos/.store in=\dgrposition,
at pos={(0,0)},
height/.store in=\dgrheight,
height=10cm,
min value/.store in=\minval,
min value=0,
max value/.store in=\maxval,
max value=10,
step/.store in=\incstep,
step=1,
horizontal rule width/.store in=\horulewidth,
horizontal rule width=1cm,
min step/.store in=\minstep,
min step={\incstep/2},
minor tick rule width/.store in=\minortickrulewidth,
minor tick rule width=3mm,
little tick rule width/.store in=\litteltickrulewidth,
little tick rule width=1mm,
tick direction/.store in=\tickpos,
tick direction=left,
label above/.store in=\lababove,
label above={},
label sloped/.store in=\labsloped,
label sloped={},
label above rotation/.store in=\rotation,
label above rotation=0,
diagram/.code={
\path \dgrposition node(A){};
\pgfgetlastxy{\xA}{\yA};
\ifscalemaxtomin%
\pgfmathsetmacro\sndval{\maxval-\incstep}
\foreach \y[count=\yi from 0] in {\maxval,\sndval,...,\minval}{
\global\let\maxitems\yi%
}%
\else%
\pgfmathsetmacro\sndval{\minval+\incstep}
\foreach \y[count=\yi from 0] in {\minval,\sndval,...,\maxval}{
\global\let\maxitems\yi%
}%
\fi
\draw(\xA,\yA)--++(0,\dgrheight)
node[pos=0.5,sloped,above]{\labsloped}
node[above,rotate=\rotation,transform shape]{\lababove};% vertical line+above label
\pgfmathsetmacro\actualstep{\dgrheight/\maxitems}%
% little ticks
\pgfmathsetmacro\littletickstep{\actualstep/10}
\foreach \y in {0,\littletickstep,...,\dgrheight}{
\ifnum\pdf@strcmp{\tickpos}{left}=\z@%
\draw(\xA,\yA+\y pt) --++(-\litteltickrulewidth,0);
\fi%
\ifnum\pdf@strcmp{\tickpos}{right}=\z@%
\draw(\xA,\yA+\y pt) --++(\litteltickrulewidth,0);
\fi%
}%
% min step
\pgfmathsetmacro\mintickstep{\actualstep/2}
\ifscalemaxtomin%
\foreach \y [count=\yi from 0,
evaluate=\yi as \ytext using ((\maxval-\yi*\incstep+\yi*\incstep/2))]
in {0,\mintickstep,...,\dgrheight}{%
\ifnumodd{\yi}{% true
\ifnum\pdf@strcmp{\tickpos}{left}=\z@%
\draw (\xA,\yA+\y pt) --++(-\minortickrulewidth,0)
node[left,font=\footnotesize]{\pgfmathprintnumber{\ytext}};
\fi%
\ifnum\pdf@strcmp{\tickpos}{right}=\z@%
\draw (\xA,\yA+\y pt) --++(\minortickrulewidth,0)
node[right,font=\footnotesize]{\pgfmathprintnumber{\ytext}};
\fi%
}{}%
}%
\else%
\foreach \y [count=\yi from 0,
evaluate=\yi as \ytext using ((\yi*\incstep+2*\minval)/2)] in
{0,\mintickstep,...,\dgrheight}{%
\ifnumodd{\yi}{% true
\ifnum\pdf@strcmp{\tickpos}{left}=\z@%
\draw (\xA,\yA+\y pt) --++(-\minortickrulewidth,0)
node[left,font=\footnotesize]{\pgfmathprintnumber{\ytext}};
\fi%
\ifnum\pdf@strcmp{\tickpos}{right}=\z@%
\draw (\xA,\yA+\y pt) --++(\minortickrulewidth,0)
node[right,font=\footnotesize]{\pgfmathprintnumber{\ytext}};
\fi%
}{}%
}%
\fi%
% main step
\ifscalemaxtomin%
\ifscalefromzero%
\foreach \y[count=\yi from 0,
evaluate=\yi as \ytext using (\maxval-\yi*\incstep)] in
{0,\actualstep,...,\dgrheight}{%
\ifnum\pdf@strcmp{\tickpos}{left}=\z@%
\pgfkeys{/pgf/number format/.cd,fixed,precision=2}
\draw (\xA,\yA+\y pt) --++(-\horulewidth,0)
node[left]{\pgfmathprintnumber{\ytext}};
\fi%
\ifnum\pdf@strcmp{\tickpos}{right}=\z@%
\pgfkeys{/pgf/number format/.cd,fixed,precision=2}
\draw (\xA,\yA+\y pt) --++(\horulewidth,0)
node[right]{\pgfmathprintnumber{\ytext}};
\fi%
}%
\else%
\foreach \y[count=\yi from 0,
evaluate=\yi as \ytext using (\maxval-\yi*\incstep)] in
{0,\actualstep,...,\dgrheight}{%
\ifnum\pdf@strcmp{\tickpos}{left}=\z@%
\pgfkeys{/pgf/number format/.cd,fixed,precision=2}
\draw (\xA,\yA+\y pt) --++(-\horulewidth,0)
node[left]{\pgfmathprintnumber{\ytext}};
\fi%
\ifnum\pdf@strcmp{\tickpos}{right}=\z@%
\pgfkeys{/pgf/number format/.cd,fixed,precision=2}
\draw (\xA,\yA+\y pt) --++(\horulewidth,0)
node[right]{\pgfmathprintnumber{\ytext}};
\fi%
}%
\fi%
\else%
\ifscalefromzero%
\foreach \y[count=\yi from 0,
evaluate=\yi as \ytext using (\yi*\sndval+\minval)] in
{0,\actualstep,...,\dgrheight}{%
\ifnum\pdf@strcmp{\tickpos}{left}=\z@%
\pgfkeys{/pgf/number format/.cd,fixed,precision=2}
\draw (\xA,\yA+\y pt) --++(-\horulewidth,0)
node[left]{\pgfmathprintnumber{\ytext}};
\fi%
\ifnum\pdf@strcmp{\tickpos}{right}=\z@%
\pgfkeys{/pgf/number format/.cd,fixed,precision=2}
\draw (\xA,\yA+\y pt) --++(\horulewidth,0)
node[right]{\pgfmathprintnumber{\ytext}};
\fi%
}%
\else%
\foreach \y[count=\yi from 0,
evaluate=\yi as \ytext using (\yi*\incstep+\minval)] in
{0,\actualstep,...,\dgrheight}{%
\ifnum\pdf@strcmp{\tickpos}{left}=\z@%
\pgfkeys{/pgf/number format/.cd,fixed,precision=2}
\draw (\xA,\yA+\y pt) --++(-\horulewidth,0)
node[left]{\pgfmathprintnumber{\ytext}};
\fi%
\ifnum\pdf@strcmp{\tickpos}{right}=\z@%
\pgfkeys{/pgf/number format/.cd,fixed,precision=2}
\draw (\xA,\yA+\y pt) --++(\horulewidth,0)
node[right]{\pgfmathprintnumber{\ytext}};
\fi%
}%
\fi%
\fi%
}%
}

% that's just an alias for \node
\def\drawnomogrampart{\tikz@path@overlay{node}}
\makeatother

\begin{document}

\begin{tikzpicture}
\drawnomogrampart[/nomogram/single diagram/.cd,
height=7cm,
min value=50,
max value=130,
step=10,
label above=$\Delta v$,
scale from zero=false,
diagram]{};
\begin{scope}[rotate=-30,transform shape]
\drawnomogrampart[/nomogram/single diagram/.cd,
horizontal rule width=0.5cm,
height=8cm,
min value=0,
max value=0.901,% for rounding purposes
step=0.1,
label above rotation=30,
label above={$M_f=1-\textrm{e}^{-\Delta v/9.81*I_{sp}}$},
label sloped={Mass Fraction $(M_f)$},
tick direction=right,
diagram]{};
\end{scope}
\drawnomogrampart[/nomogram/single diagram/.cd,
scale from zero=false,
height=7cm,
scale max to min=true,
min value=400,
max value=2000,
step=200,
at pos={(7,0)},
tick direction=right,
label above=IPS (s),
diagram]{};
\end{tikzpicture}
\end{document}

The result:

-
I'm impressed ! I would have the to try it myself. Just a remark, the upper corner of the slanted scale is not quiet aligned with the top of other scales. Maybe I'm wrong. – Tarass May 8 '14 at 11:27
@Tarass: glad you like it :) Telling truth, the current approach leave to the user to "arrange" the heights of each part such that they align. However, I should come back on this later on to fix some issues, so I can have a look to that as well. – Claudio Fiandrino May 8 '14 at 12:19
There one problem : if you set min value to something else as 0, max value changes too. – Tarass May 8 '14 at 13:03
@Tarass: that's the main issue; the second one is that you can't use the max-to-min sense. A third one is about the scale, that is if you set the max to 4500 with a step of 500 the max will be 4000 only. I'm aware of these main problems as well other minor ones ;) If you want, feel free to fix them and to edit my answer. I'm busy right now, so I can't work out immediately a solution. Perhaps late in the afternoon or tomorrow. – Claudio Fiandrino May 8 '14 at 13:08
I will fix the remaining problems in the next days and provide more details illustrating all keys! – Claudio Fiandrino May 15 '14 at 17:19

Due to precision problems with calculation with TikZ, I change the approach and use a small pearl script to do this calculations. Run with pdflatex -shell-script

I use decorations.markings library to do graduations on every type of path. (No more accumulations of marks problem).

I tried to put some explainations in the code.

The code :

\documentclass[tikz,margin=2pt]{standalone}
\usetikzlibrary{calc}
\usetikzlibrary{decorations.markings,intersections}

%%%%                        ---- Use path several times
%%%%                        ---- thanks to Andrew Stacey
\makeatletter
\tikzset{
use path for main/.code={%
\expandafter\pgfsyssoftpath@setcurrentpath\csname tikz@intersect@path@name@#1\endcsname
}%
},
use path for actions/.code={%
\expandafter\def\expandafter\tikz@preactions\expandafter{\tikz@preactions\expandafter\let\expandafter\tikz@actions@path\csname tikz@intersect@path@name@#1\endcsname}%
},
use path/.style={%
use path for main=#1,
use path for actions=#1,
}
}

\pgfkeys{/MonoG/.cd,
% foot of the scale
path/.store in=\p@th,
path={(0,0)--(0,10)},
% minimale value
min/.store in=\Min,
min=0,
% maximale value
max/.store in=\Max,
max=10,
step/.store in=\Step,
step=1,
% Big ticks
% tick length
big ticks/.store in=\BTick,
big ticks=.7,
% font (whatever compatible with tikz font parameter)
big ticks font/.store in=\BFont,
big ticks font=\small,
% line width
big ticks width/.store in=\BWidth,
big ticks width=thick,
% medium ticks (same choices)
med ticks/.store in=\MTick,
med ticks=.4,
med ticks font/.store in=\MFont,
med ticks font=\footnotesize,
med ticks width/.store in=\MWidth,
med ticks width=thin,
% small ticks
small ticks/.store in=\STick,
small ticks=.2,
small ticks width/.store in=\SWidth,
small ticks width=very thin,
% number of sublevel of small tick
small ticks level/.store in=\STlevel,
small ticks level=1,
% tick direction
tick direction/.store in=\TickPos,
tick direction=right,
% bottom label
bottom label/.store in=\BotLabel,
bottom label={},
% midway label
midway label/.store in=\MidLabel,
midway label={},
% top label
top label/.store in=\TopLabel,
top label={},
reverse/.store in=\Reverse,
reverse=false,
diagram/.code={%

% draw the path and place the nodes for labelling
\draw[\BWidth,name path=Path] \p@th
node[pos=1,above] (MnAbove) {\TopLabel}
node[midway,sloped,above] (MnMid) {\MidLabel}
node[pos=0,below] (MnBelow) {\BotLabel} ;

% left right position flag
\ifnum\pdf@strcmp{\TickPos}{left}=\z@%
\def\TPos{1}
\fi%
\ifnum\pdf@strcmp{\TickPos}{right}=\z@%
\def\TPos{-1}
\fi%

% reverse scale flag
\def\Rev{1}
\ifnum\pdf@strcmp{\Reverse}{true}=\z@%
\def\Rev{-1}
\fi%

% set the style for the tick labels
\tikzset{Big/.style={font=\BFont,\TickPos,transform shape,rotate=-90},
Med/.style={font=\MFont,\TickPos,transform shape,rotate=-90},
/pgf/decoration/reset marks % Reset marks each time
% to avoid accumulation problems
} ;

% Call of external calculation script
\immediate\write18{%
./script.pl     \Min\space%
\Max\space%
\Step\space%
\STlevel\space\STick\space > Sortie.tex}

\input{Sortie}
}
}
\makeatother

\begin{document}

\begin{tikzpicture}
\path[/MonoG/.cd,
step=5,
min=5,
max=25.01,
path={(4,0) parabola (8,10)},
diagram
] ;

\path[/MonoG/.cd,
step=1000,
max=4500,
tick direction=left,
small ticks=.2,
small ticks level=2,
top label=$\Delta_V$,
diagram
] ;

% to avoid 5.10e-2
\pgfkeys{/pgf/number format/.cd,fixed,precision=3}

\path[/MonoG/.cd,
path={(11,0)--(11,10)},
step=100,
max=800,
min=199.9,
reverse=true,
small ticks level=2,
small ticks = .3,
top label=IPS (s),
diagram
] ;

\path[/MonoG/.cd,
step=.1,
max=.9,
path={(0,0)--(4,10)},
midway label=Mass Fraction $(M_f)$,
top label={$M_f=1-\textrm{e}^{-\Delta v/9.81*I_{sp}}$},
diagram
] ;

\end{tikzpicture}
\begin{tikzpicture}
\path[/MonoG/.cd,
step=10,
max=12,
small ticks level=2,
diagram
] ;

\path[/MonoG/.cd,
step=1,
max=10.01,
min=0,
tick direction=left,
diagram
] ;

\path[/MonoG/.cd,
path={(3,0)--(3,10)},
min=.2,
step=5,
max=9.7,
diagram
] ;

\path[/MonoG/.cd,
tick direction=left,
path={(3,0)--(3,10)},
min=-5,
step=5,
max=15,
diagram
] ;

\path[/MonoG/.cd,
path={(12,0) arc (0:180:3)},
min=0,
step=30,
small ticks level=2,
max=180.01,
diagram
] ;
\end{tikzpicture}

\end{document}

The pearl script

#!/usr/bin/perl -w

use POSIX "fmod" ;

$Min =$ARGV[0] ;       # Minimum
$Max =$ARGV[1] ;       # Maximum
$Step =$ARGV[2] ;      # Step of graduation
$Level =$ARGV[3] ;    # level of small ticks default 1
$TickL =$ARGV[4] ;    # length of small ticks (1st level)

$Coeff =$Max-$Min ; # Scale on the path from 0 to 1$Begin = $Min - fmod($Min,$Step) ; # Calculation of the place if ($Begin < $Min ) { # of the first Label$Begin += $Step ; }$X = $Begin ;$Y =  ($X -$Min) / $Coeff ; print "\\begin{scope}[decoration={markings," ; while ($X <= $Max) { print "mark=at position$Y*\\Rev with {\\draw[\\BWidth] (0,0) --++ (0,\\BTick*\\TPos) node[Big] {\\pgfmathprintnumber{$X}} ;},\n" ;$X += $Step ;$Y = ($X -$Min) / $Coeff ; } print "}]\n" ; print "\\draw[postaction={decorate},use path=Path];\n" ; print "\\end{scope}\n" ; ## half step$X = $Begin -$Step/2 ;             # Calculation of
if ( $X <$Min ) { $X +=$Step ; }  # the first Label
$Y = ($X - $Min) /$Coeff ;

print "\\begin{scope}[decoration={markings," ;
while ($X <=$Max) {
print "mark=at position $Y*\\Rev with {\\draw[\\MWidth] (0,0) --++ (0,\\MTick*\\TPos) node[Med] {\\pgfmathprintnumber{$X}} ;},\n" ;
$X +=$Step ;
$Y = ($X - $Min) /$Coeff ;
}
print "}]\n" ;
print "\\draw[postaction={decorate},use path=Path];\n" ;
print "\\end{scope}\n" ;

## 10e ...
$Step /= 10 ;$Begin = $Min - fmod($Min,$Step) ; # Calculation of if ($Begin < $Min ) { # the first Label$Begin += $Step ; } if ($Begin < $Min ) {$Begin += $Step ; } # the first Label for (my$i = 1; $i <=$Level ; $i++) {$X = $Begin ;$Y = ($X -$Min) / $Coeff ; print "\\begin{scope}[decoration={markings," ; while ($X <= $Max) { print "mark=at position$Y*\\Rev with {\\draw[very thin] (0,0) --++ (0,$TickL*\\TPos) ;},\n" ;$X += $Step ;$Y = ($X -$Min) / $Coeff ; } print "}]\n" ; print "\\draw[postaction={decorate},use path=Path];\n" ; print "\\end{scope}\n" ;$Step /= 2 ;
\$TickL /= 2 ;
}
-
Actually,if you change the steps to 10, then 12 will be rounded. your example has changed steps to 1 so 12 will not be rounded. so the question is to keep the fraction of max/steps. – Beatlej May 14 '14 at 21:22
@JinjianWang this problem is solved by now, as one can see on the second picture. – Tarass May 17 '14 at 8:40