48

How can I create a Caesar's encryption disk with LaTeX?

Caesar's encryption disk

I guess, the ultimate solution would take a arbitrary list of letters and calculate the spaces and angles for each letter automatically. So if I pass 4 letters to it, each space should have 90 degrees.

doncherry
  • 54,637
Uwe Ziegenhagen
  • 13,168
  • 5
  • 53
  • 93

4 Answers4

57

Commands

  • \drawCaesarsDisk that works with a number of letters (only A to Z)
  • \drawCaesarsList that works with one list for both rings or two different lists

Keys

  • inner radius/middle radius/outer radius
    • Improvements: Specify inner radius and add inner height and outer height respectively to calculate the actual radii.
    • Improvements: With these heights, scale the letters so that they fit right into one ring segment.
  • number of letters (only \drawCaesarsDisk)
  • inner letters and outer letters (only \drawCaesarsList): If only inner letters is specified the letters in the outer ring will be the same as in the inner ring.
  • shift

Parameters (both commands)

  • First (optional): Key=Values for the disk.
  • Second (mandatory): where the Disk is placed.

Improvement:

  • Radii/font-size calculation (see above)
  • Starting with the first letter at the north position or right of it (possible style)
  • Adding styles for circles, separators, letters, etc.
  • Bug: The List command breaks with

    ! Dimension too large. <recently read> \pgfmath@x
    

    when 46 or more letters are specified.

  • I forgot something …

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{backgrounds}
\makeatletter
\tikzset{
    /caesar/.cd,
    inner radius/.store in=\qrr@caesar@innerR,
    middle radius/.store in=\qrr@caesar@middleR,
    outer radius/.store in=\qrr@caesar@outerR,
    inner letters/.store in=\qrr@caesar@innerL,
    outer letters/.store in=\qrr@caesar@outerL,
    number of letters/.code=\pgfmathtruncatemacro\qrr@caesar@number{#1},
    shift/.store in=\qrr@caesar@shift,
    % defaults:
    outer letters=,
    shift=0
}
\newcommand*{\drawCaesarsDisk}[2][]{%
    \begingroup
    \pgfqkeys{/caesar}{#1}%
    \foreach \radius in {\qrr@caesar@innerR,\qrr@caesar@middleR,\qrr@caesar@outerR}
        \draw (#2) circle [radius=\radius];
    \foreach \step in {0,...,\numexpr\qrr@caesar@number-1}{
        \draw[shift={(#2)}] (\step*360/\qrr@caesar@number:\qrr@caesar@innerR) -- (\step*360/\qrr@caesar@number:\qrr@caesar@outerR);
        \node[shift={(#2)},rotate=(\step+.5)*360/\qrr@caesar@number-90] at ({(\step+.5)*360/\qrr@caesar@number}:{.5*(\qrr@caesar@innerR)+.5*(\qrr@caesar@middleR)} ) {\@Alph{\numexpr26-\step}};
        \pgfmathtruncatemacro\pgf@temp{mod(\step+\qrr@caesar@shift,\qrr@caesar@number)}%
        \node[shift={(#2)},rotate=(\step+.5)*360/\qrr@caesar@number-90] at ({(\step+.5)*360/\qrr@caesar@number}:{.5*(\qrr@caesar@outerR)+.5*(\qrr@caesar@middleR)} ) {\@Alph{\numexpr26-\pgf@temp}};
    }
    \endgroup
}
\newcount\qrr@caesar@c
\newcommand*{\drawCaesarsList}[2][]{%
    \begingroup
    \pgfqkeys{/caesar}{#1}%
    \foreach \radius in {\qrr@caesar@innerR,\qrr@caesar@middleR,\qrr@caesar@outerR}
        \draw (#2) circle [radius=\radius];
    \qrr@caesar@c=0\relax
    \foreach \element in \qrr@caesar@innerL {\global\advance\qrr@caesar@c1}
    \ifx\pgfutil@empty\qrr@caesar@outerL
        \let\qrr@caesar@outerL\qrr@caesar@innerL
    \fi
    \edef\qrr@caesar@number{\number\qrr@caesar@c}%
    \foreach \innerLetter[count=\step from 0] in \qrr@caesar@innerL {
        \draw[shift={(#2)}] (\step*360/\qrr@caesar@number:\qrr@caesar@innerR) -- (\step*360/\qrr@caesar@number:\qrr@caesar@outerR);
        \node[shift={(#2)},rotate=-(\step+.5)*360/\qrr@caesar@number-90] at ({-(\step+.5)*360/\qrr@caesar@number}:{.5*(\qrr@caesar@innerR)+.5*(\qrr@caesar@middleR)} ) {\innerLetter};
    }
    \foreach \outerLetter[count=\step@ from 0] in \qrr@caesar@outerL {
        \ifnum\step@=\qrr@caesar@number\breakforeach\fi
        \pgfmathtruncatemacro\step{mod(\step@+\qrr@caesar@shift,\qrr@caesar@number)}%
        \node[shift={(#2)},rotate=-(\step+.5)*360/\qrr@caesar@number-90] at ({-(\step+.5)*360/\qrr@caesar@number}:{.5*(\qrr@caesar@outerR)+.5*(\qrr@caesar@middleR)} ) {\outerLetter};
    }
    \endgroup
}
\makeatother
\begin{document}
\begin{tikzpicture}
\drawCaesarsDisk[
    inner radius=2cm,
    middle radius=2cm+1.3em,
    outer radius=2cm+3em,
    number of letters=26,
    shift=13,
    ]{0,0}
\end{tikzpicture}

\begin{tikzpicture}
\drawCaesarsList[
    inner radius=.5cm,
    middle radius=.5cm+1.3em,
    outer radius=.5cm+3em,
    inner letters={a,b,c,d,e,f,g},
    outer letters={A,B,C,D,E,F,G}
    ]{2,0}
\end{tikzpicture}

\begin{tikzpicture}
\drawCaesarsList[
    inner radius=2cm,
    middle radius=2cm+1.3em,
    outer radius=2cm+3em,
    inner letters={a,...,z,A,B,...,T},%,U,V,W,X,Y,Z},
    shift=26,
    ]{0,0}
\end{tikzpicture}
\end{document}

Output

enter image description here

enter image description here

enter image description here

Qrrbrbirlbel
  • 119,821
  • Wow! However I get an error: "! Package tikz Error: A node must have a (possibly empty) label text. ...

    l.58 ]{0,0}

    – Uwe Ziegenhagen Mar 20 '13 at 07:17
  • @UweZiegenhagen I have added some explanation since you made your comment. I also updated the solution to work with the 2.10 version of TikZ. The problem was a parsing error that was already discussed in other questions. And to clarify: Yes, you can give two (possibly different) lists and you can specify a shift. – Qrrbrbirlbel Mar 20 '13 at 07:35
  • You are awesome! :-) – Uwe Ziegenhagen Mar 20 '13 at 07:47
  • An alternative way of getting the length of a \foreach list is: \foreach \x [count=\c from 1, remember=\c] in {A,...,Z}{}. Then \c holds the length of the list. – Mark Wibrow Mar 20 '13 at 11:50
22

Here is what I've done so far:

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
  \begin{tikzpicture}
    \pgfmathsetmacro{\alphsize}{26}

    \pgfmathsetmacro{\ang}{360/\alphsize}
    \pgfmathsetmacro{\d}{10}
    \pgfmathsetmacro{\op}{98 + \ang/2 - 1.2}
    \pgfmathsetmacro{\e}{\ang + \ang*\d}
    \pgfmathsetmacro{\ep}{\op + \ang*\d}

    \foreach \x in {0,\ang,...,360} {
      \draw[gray] (\x:8em) -- (\x:12em);
    }

    \foreach \x [count=\xi] in {A,...,Z} {
      \node[rotate=\ang - \ang*\xi] at (\op - \ang*\xi:11em) {\Large\x};
      \node[rotate=\e - \ang*\xi] at (\ep - \ang*\xi:9em) {\Large\x};
    }

    \draw[thick] (0cm,0cm) circle(12em);
    \draw[gray] (0cm,0cm) circle(10em);
    \draw[thick] (0cm,0cm) circle(8em);
  \end{tikzpicture}
\end{document}

Caesar latin circle

m0nhawk
  • 9,664
18

Here:

\documentclass{article}
\usepackage{tikz}
\usepackage{ifthen}

\newcounter{encrypted}
\newcounter{original}

\newcommand{\increase}[1]{%command to increase a counter by 1 modulo 26
\ifthenelse{\arabic{#1}<26}{\addtocounter{#1}{1}}{\setcounter{#1}{1}}
}

\begin{document}

\setcounter{encrypted}{7}
\setcounter{original}{1}
\begin{tikzpicture}[scale=0.5]
\draw(0,0)circle(5)circle(7)circle(9);
\foreach \x in {1,2,...,26}
{
\draw(\x*360/26:5)--(\x*360/26:9);
\node at (\x*360/26+360/26+180/26:6){\Alph{encrypted}};
\node at (\x*360/26+360/26+180/26:8){\Alph{original}};
\increase{encrypted}
\increase{original}
}
\end{tikzpicture}

\end{document}

enter image description here

hpesoj626
  • 17,282
Toscho
  • 4,713
15

Just 4 fun with PSTricks.

enter image description here

\documentclass[pstricks,border=12pt]{standalone}
\SpecialCoor
\makeatletter
\def\N{26}

\begin{document}
% speficy the angular distance between the 2 sets of alphabets
\def\offset{13}
\begin{pspicture}(-5,-5)(5,5)
    \psforeach{\r} {2,3,4}{\pscircle{\r}}
    \degrees[\N]
    \psforeach{\t}{65,66,..,90}{%
        \psline(2;\the\psLoopIndex)(4;\the\psLoopIndex)
        \pstVerb{/angle {\the\psLoopIndex\space 0.5 add} bind def}%
        \rput{!angle 6.5 sub}(!3.5 angle \pst@angleunit PtoC){\char\t\relax}
        \rput{!angle \offset\space add 6.5 sub}(!2.5 angle \offset\space add \pst@angleunit PtoC){\char\t\relax}
    }
\end{pspicture}
\end{document}

Animated version

To be funner!

enter image description here

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{multido}
\SpecialCoor
\makeatletter
\def\N{26}

\begin{document}
\multido{\i=0+1}{\N}{%
% speficy the angular distance between the 2 sets of alphabets
\def\offset{\i}
\begin{pspicture}(-5,-5)(5,5)
    \psforeach{\r} {2,3,4}{\pscircle{\r}}
    \degrees[\N]
    \psforeach{\t}{65,66,..,90}{%
        \psline(2;\the\psLoopIndex)(4;\the\psLoopIndex)
        \pstVerb{/angle {\the\psLoopIndex\space 0.5 add} bind def}%
        \rput{!angle 6.5 sub}(!3.5 angle \pst@angleunit PtoC){\char\t\relax}
        \rput{!angle \offset\space add 6.5 sub}(!2.5 angle \offset\space add \pst@angleunit PtoC){\char\t\relax}
    }
\end{pspicture}}
\end{document}

Warning

If we invoke \degrees[<value other than 360>] previously, conversion with \pst@angleunit is needed for representing points in RPN notation but it is NOT need for representing rotation angle. See the following code snippet.

\rput{!angle 6.5 sub}(!3.5 angle \pst@angleunit PtoC){\t}

It is funny? Don't ask me! I just knew this feature several minutes ago!

One more feature, unlike \foreach \t in {A,...,Z}{} that produces a correct result, \psforeach{\t}{A,..,Z}{} produces a weird output!