Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

Nomogram for the rocket equation

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?

Additional info

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

More about nomography: http://www.slideshare.net/arulalan/nomography

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

2 Answers 2

up vote 30 down vote accepted

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:

enter image description here

share|improve this answer
    
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 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 at 12:19
    
There one problem : if you set min value to something else as 0, max value changes too. –  Tarass May 8 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 at 13:08
    
I will fix the remaining problems in the next days and provide more details illustrating all keys! –  Claudio Fiandrino May 15 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.

enter image description here

enter image description here

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={%
        \tikz@addmode{%
          \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,
    % Major graduation Step
    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 graduation    
    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 ;
    }
share|improve this answer
    
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 at 21:22
    
@JinjianWang this problem is solved by now, as one can see on the second picture. –  Tarass May 17 at 8:40

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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