1

I'm attempting to produce something similar to the given image. However, regarding the "zig-zag" arrows in between the concentric circles, I want these zig-zags to themselves outline an arc of a circle with a radius halfway between the two bounding concentric circles.

enter image description here

Thus far I have the following MWE (note that the node placement is not important right now)

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows}

\begin{document}
    \begin{tikzpicture}[scale=0.7,rotate=90]
        % outer circle
        \def \radius {5}
            \draw (0,0) circle (5);
                \foreach \angle / \label in {0/0, 30/11, 60/10, 90/9, 120/8, 150/7, 180/6, 210/5, 240/4, 270/3, 300/2, 330/1}
                    {
                        \node[draw,fill=gray!30,circle] at (\angle:\radius) {\label};
                    }
        % middle circle
        \def \n {7}
        \def \radius {3}
            \draw (0,0) circle (\radius);
                \foreach \s in {1,...,\n}
                    {
                        \node[draw,fill=black,circle] at ({360/\n * (\s - 1)}:\radius) {};
                    }
        % inner circle
        \def \n {3}
        \def \radius {1}
            \draw (0,0) circle (\radius);
                \foreach \s in {1,...,\n}
                    {
                        \node[draw,fill=white,circle] at ({360/\n * (\s - 1)}:\radius) {};
                    }
        % arrow
        \draw[->] (0,0) -- (6,0);
        \draw[->] (0:0) -- (240:6);
    \end{tikzpicture}

\end{document}

This produces the following:

enter image description here

The "12 o'clock" arrow here is fine; the problem is with the 4 o'clock arrow, which should actually exit the outer circle at 3.

The Request: Ideally this arrow will:

  1. Exit the white circle node;
  2. Halfway (radius 2 in the MWE) before reaching the middle circle (the one with the black nodes), the arrow will turn counterclockwise until it is in line with the closest black circle node in the middle circle. But this turn should be an arc, not a straight line;
  3. Proceed through this black circle node;
  4. Repeat Step 2, this time in relation to the "3" node.

Edit: Here's a sample image to show the type of arrow I'm looking for:

enter image description here

  • I'm honestly not sure what you're asking. Can you produce a mock-up version of your desired result? – Alenanno Jun 19 '16 at 18:48
  • I considered it originally, but I didn't want to bog the post down with too many images; it's in there now! – Richard Jun 19 '16 at 18:58
  • Do you really want to go anticlockwise even if clockwise gets you to the closest black node faster? To go anticlockwise, you have to go in almost a complete circle in some cases, which looks odd to me. But, as I say below, without know what is being represented, strange-to-me is not very meaningful. – cfr Jun 19 '16 at 20:47
3

The zig-zag-arc line can be done with the calc library:

\tikzset{
  zigzagarc/.default={0,0},
  zigzagarc/.style={to path={
      let \p0=($(\tikztostart)-(#1)$),
          \p1=($(\tikztotarget)-(#1)$),
          \n0={atanXY(\p0)}, \n1={atanXY(\p1)},
        % \n0={atan2(\y0,\x0)}, \n1={atan2(\y1,\x1)},
          \n d={.5*veclen(\p1)-.5*veclen(\p0)},
          \n r={.5*veclen(\p1)+.5*veclen(\p0)} in
      -- ++ (\n0:\n d) arc [radius=\n r, start angle=\n0, end angle=\n1] \tikztonodes
      -- (\tikztotarget)}}}

I'm using a modified atan2 function called atanXY defined as

\pgfmathdeclarefunction{atanXY}{2}{\pgfmathatantwo@{#2}{#1}}

so that it is still possible, to do atanXY(\p?) as calc's \p macro just expands to \x?, \y? (this makes it you can use it on a path without using \x and \y. (Cf. A244619)

The alternative without atanXY is also given.

Then you can just use (<p1>) to[zigzagarc] (<p2>) and it draws a straight line away from (0,0), an arc and the rest of the line.

A parameter to zigzagarc allows it to use another center for the arcs but (0,0).

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{calc,arrows.meta}
\tikzset{
  zigzagarc/.default={0,0},
  zigzagarc/.style={to path={
      let \p0=($(\tikztostart)-(#1)$),
          \p1=($(\tikztotarget)-(#1)$),
          \n0={atanXY(\p0)}, \n1={atanXY(\p1)},
        % \n0={atan2(\y0,\x0)}, \n1={atan2(\y1,\x1)},
          \n d={.5*veclen(\p1)-.5*veclen(\p0)},
          \n r={.5*veclen(\p1)+.5*veclen(\p0)} in
      -- ++ (\n0:\n d) arc [radius=\n r, start angle=\n0, end angle=\n1] \tikztonodes
      -- (\tikztotarget)}}}
\makeatletter
% for easy usage of calc's atanXY(\p?):
\pgfmathdeclarefunction{atanXY}{2}{\pgfmathatantwo@{#2}{#1}}

\tikzset{if/.code n args=3{\pgfmathparse{#1}\ifnum\pgfmathresult=0\relax
  \expandafter\pgfutil@firstoftwo\else\expandafter\pgfutil@secondoftwo\fi
  {\pgfkeysalso{#3}}{\pgfkeysalso{#2}}}}
\makeatother
\newcommand*\tvo[1]{\pgfkeysvalueof{/tikz/#1}}% tikzvalueof
\begin{document}
\begin{tikzpicture}[scale=.7,
  declare function={posonarc(\x,\n)=360/\n*\x;},
  n-0/.initial=3,  n-1/.initial=7,  n-2/.initial=12,
  d-0/.initial=80, d-1/.initial=15, d-2/.initial=90,
  every snode/.style={shape=circle, draw, inner sep=+0pt, minimum size=+5pt},
  s-0/.style={every snode, node contents=,fill=white},
  s-1/.style={every snode, node contents=,
    if={or(#1==0,or(#1==1,or(#1==3,#1==5)))}{fill=black}{fill=white}},
  s-2/.style={every snode, shape=circle, draw, inner sep=+1pt,
    fill=white, node contents={#1}, fill=gray!50, minimum size=1.2em,
    if={or(#1==4,#1==7)}{fill=white}
      {if={#1==10}{node contents=$t$}
        {if={#1==11}{fill=white, node contents=$e$}{}}}},
  ]
\foreach \r in {1,...,3} \draw circle[radius=\r];

\foreach \ring in {0,...,2}
  \foreach \x[count=\xx from 0] in {1,...,\tvo{n-\ring}}
    \node (n-\ring-\x) at ({\tvo{d-\ring}-posonarc(\xx,\tvo{n-\ring})}:
      \ring+1) [s-\ring=\xx];
\foreach \a/\b/\c in {1/6/12, 2/2/5, 3/4/8}%    down here!
  \draw[zigzagarc] (n-0-\a) to (n-1-\b) to (n-2-\c)
    -- ++ ({\tvo{d-2}-posonarc(\c-1,\tvo{n-2})}:1) [-Latex];
\end{tikzpicture}
\end{document}
  • This is exactly what I'm looking for, and it actually saves me some work with figuring some other things out in the near future. I'll need some time to study this code to figure out exactly what's going on; you may hear from me again soon, if you don't mind. Thanks! – Richard Jun 20 '16 at 11:06
4

Initially, I took the anticlockwise desideratum seriously. From your mock-up, I'm no longer sure it is what you want, so here's an alternative. The original is below.

Updated Parameterised Answer Following Mock-Up

If you really want to go anticlockwise, that will sometimes mean drawing an arc through 359 degrees. This is true even if the line always passes through a white node. The closest route to the closest black node if exiting the white node at 240, for example, is clockwise to 3 and not anticlockwise to 4. I'm not sure if that is what you really want as it seems strange. Below, I've taken the shortest route, but I don't know if that is correct.

This is, of course, the trouble with trying to draw something which represents I-know-not-what. How-it-should-go is probably obvious to you, but entirely obscure to me!

\documentclass[tikz,border=10pt,multi]{standalone}
\usetikzlibrary{arrows.meta}
\begin{document}
% ateb i gwestiwn Richard: http://tex.stackexchange.com/q/315559/
\begin{tikzpicture}
  [
    scale=0.7,
    rotate=90,
    >=Triangle,
    outer radius/.store in=\oradius,
    middle radius/.store in=\mradius,
    inner radius/.store in=\iradius,
    outer no/.store in=\ono,
    middle no/.store in=\mno,
    inner no/.store in=\ino,
    outer number/.store in=\onumber,
    middle number/.store in=\mnumber,
    inner number/.store in=\inumber,
    outer n/.code={%
      \pgfmathsetmacro\tno{int(#1-1)}
      \tikzset{%
        outer no/.expanded=\tno,
        outer number=#1,
      }
    },
    middle n/.code={%
      \pgfmathsetmacro\tno{int(#1-1)}
      \tikzset{%
        middle no/.expanded=\tno,
        middle number=#1,
      }
    },
    inner n/.code={%
      \pgfmathsetmacro\tno{int(#1-1)}
      \tikzset{%
        inner no/.expanded=\tno,
        inner number=#1,
      }
    },
    outer radius=5,
    middle radius=3,
    inner radius=1,
    outer n=12,
    middle n=7,
    inner n=3,
  ]
  % outer circle
  \draw (0,0) circle (\oradius);
  \foreach \i in {0,...,\ono}
  {
    \node (o\i) [draw, fill=gray!30, circle] at (-{\i*360/\onumber}:\oradius) {\i};
  }
  % middle circle
  \draw (0,0) circle (\mradius);
  \foreach \s in {0,...,\mno}
  {
    \node (m\s) [draw, fill=black, circle] at ({360*\s/\mnumber}:\mradius) {};
  }
  % inner circle
  \draw (0,0) circle (\iradius);
  \foreach \s in {0,...,\ino}
  {
    \node (i\s) [draw, fill=white, circle] at ({360*\s/\inumber}:\iradius) {};
  }
%   \foreach \i in {0,...,\ono} \node [font=\tiny, red] at (o\i) {o\i};
%   \foreach \i in {0,...,\mno} \node [font=\tiny, red] at (m\i) {m\i};
%   \foreach \i in {0,...,\ino} \node [font=\tiny, red] at (i\i) {i\i};
  % arrow
  \draw[->] (0,0) -- ({\oradius+1},0);
  \foreach \i in {0,120,240}
  {
    \pgfmathsetmacro\n{int(round(\i*\mnumber/360))}
    \pgfmathsetmacro\m{int(round((\n*360/\mnumber)*\onumber/360))}
    \draw [->, magenta] (0,0) -- (\i:{(\iradius+\mradius)/2}) arc (\i:{\n*360/\mnumber}:{(\iradius+\mradius)/2}) -- ({\n*360/\mnumber}:{(\oradius+\mradius)/2}) arc ({\n*360/\mnumber}:{\m*360/\onumber}:{(\oradius+\mradius)/2}) -- ({\m*360/\onumber}:{\oradius+1});
  }
\end{tikzpicture}
\end{document}

parameterised version based on mock-up

Original Answer

Do you want something like this? Applying your algorithm obviously results in a different exit node in some cases because 3 is closer to the closest black node than is 4, even though 4 is obviously closer to the original direction.

circles

Here's the code for the one-off version shown:

\documentclass[tikz,border=10pt,multi]{standalone}
\usetikzlibrary{arrows.meta}
\begin{document}
\begin{tikzpicture}[scale=0.7, rotate=90, >=Triangle]
  % outer circle
  \def \radius {5}
  \draw (0,0) circle (5);
  \foreach \angle / \label in {0/0, 30/11, 60/10, 90/9, 120/8, 150/7, 180/6, 210/5, 240/4, 270/3, 300/2, 330/1}
  {
    \node (o\label) [draw,fill=gray!30,circle] at (\angle:\radius) {\label};
  }
  % middle circle
  \def \n {6}
  \def \radius {3}
  \draw (0,0) circle (\radius);
  \foreach \s in {0,...,\n}
  {
    \node (m\s) [draw,fill=black,circle] at ({360*\s/(\n+1)}:\radius) {};
  }
  % inner circle
  \def \n {2}
  \def \radius {1}
  \draw (0,0) circle (\radius);
  \foreach \s in {0,...,\n}
  {
    \node (i\s) [draw,fill=white,circle] at ({360*\s/(\n+1)}:\radius) {};
  }
%   \foreach \i in {0,...,11} \node [font=\tiny, red] at (o\i) {o\i};
%   \foreach \i in {0,...,6} \node [font=\tiny, red] at (m\i) {m\i};
%   \foreach \i in {0,...,2} \node [font=\tiny, red] at (i\i) {i\i};
  % arrow
  \draw[->] (0,0) -- (6,0);
  \pgfmathsetmacro\n{int(round(240*7/360))}
  \pgfmathsetmacro\m{int(round((\n*360/7)*12/360))}
  \draw [->] (0,0) -- (240:2)  [out=180+240,in=180+\n*360/7] to ({\n*360/7}:2) -- ({\n*360/7}:4) [out=180+\n*360/7,in=180+\m*360/12] to ({\m*360/12}:4) -- ({\m*360/12}:6);
\end{tikzpicture}
\end{document}

Parameterised Version

Here's a parameterised version:

\documentclass[tikz,border=10pt,multi]{standalone}
\usetikzlibrary{arrows.meta}
\begin{document}
% ateb i gwestiwn Richard: http://tex.stackexchange.com/q/315559/
\begin{tikzpicture}
  [
    scale=0.7,
    rotate=90,
    >=Triangle,
    outer radius/.store in=\oradius,
    middle radius/.store in=\mradius,
    inner radius/.store in=\iradius,
    outer no/.store in=\ono,
    middle no/.store in=\mno,
    inner no/.store in=\ino,
    outer number/.store in=\onumber,
    middle number/.store in=\mnumber,
    inner number/.store in=\inumber,
    outer n/.code={%
      \pgfmathsetmacro\tno{int(#1-1)}
      \tikzset{%
        outer no/.expanded=\tno,
        outer number=#1,
      }
    },
    middle n/.code={%
      \pgfmathsetmacro\tno{int(#1-1)}
      \tikzset{%
        middle no/.expanded=\tno,
        middle number=#1,
      }
    },
    inner n/.code={%
      \pgfmathsetmacro\tno{int(#1-1)}
      \tikzset{%
        inner no/.expanded=\tno,
        inner number=#1,
      }
    },
    outer radius=5,
    middle radius=3,
    inner radius=1,
    outer n=12,
    middle n=7,
    inner n=3,
  ]
  % outer circle
  \draw (0,0) circle (\oradius);
  \foreach \i in {0,...,\ono}
  {
    \node (o\i) [draw, fill=gray!30, circle] at (-{\i*360/\onumber}:\oradius) {\i};
  }
  % middle circle
  \draw (0,0) circle (\mradius);
  \foreach \s in {0,...,\mno}
  {
    \node (m\s) [draw, fill=black, circle] at ({360*\s/\mnumber}:\mradius) {};
  }
  % inner circle
  \draw (0,0) circle (\iradius);
  \foreach \s in {0,...,\ino}
  {
    \node (i\s) [draw, fill=white, circle] at ({360*\s/\inumber}:\iradius) {};
  }
%   \foreach \i in {0,...,\ono} \node [font=\tiny, red] at (o\i) {o\i};
%   \foreach \i in {0,...,\mno} \node [font=\tiny, red] at (m\i) {m\i};
%   \foreach \i in {0,...,\ino} \node [font=\tiny, red] at (i\i) {i\i};
  % arrow
  \draw[->] (0,0) -- ({\oradius+1},0);
  \foreach \j [evaluate=\j as \i using (\j*360/\mnumber)+20] in {0,...,\mno}
  {
    \pgfmathsetmacro\n{int(round(\i*\mnumber/360))}
    \pgfmathsetmacro\m{int(round((\n*360/\mnumber)*\onumber/360))}
    \draw [->, blue] (0,0) -- (\i:{(\iradius+\mradius)/2})  [out=180+\i,in=180+\n*360/\mnumber] to ({\n*360/\mnumber}:{(\iradius+\mradius)/2}) -- ({\n*360/\mnumber}:{(\oradius+\mradius)/2}) [out=180+\n*360/\mnumber,in=180+\m*360/\onumber] to ({\m*360/\onumber}:{(\oradius+\mradius)/2}) -- ({\m*360/\onumber}:{\oradius+1});
  }
\end{tikzpicture}
\end{document}

repetition in blue

  • This is a tremendous amount of work, and a terrific answer. I wish I could thank you more than just a single upvote! – Richard Jun 20 '16 at 11:05
  • @Richard Thanks. I think I misunderstood what you wanted but I guess I will leave this as parts of it may be useful to you or somebody else. – cfr Jun 20 '16 at 12:04

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.