9

I need to do a diagram in TikZ and, although the result I have look like I expected, I was wondering if there is a better/more general solution to my problem.

Basically I want to draw a node with n-connection (3 in my implementation), and repeat/continue the pattern (rotated and translated depending on which connection I want to continue the pattern).

I was wondering if it is possible to get a solution like:

 \drawGraph{(position0)}{rotation0}{[2,3,1,3,1,3,3,3,3,2]}

to get something similar to what I implemented.

Here's my current solution:

    \documentclass{article}
    \usepackage{ifthen}
    \usepackage{tikz}
    \usetikzlibrary{calc}
    \begin{document}

    \begin{tikzpicture}
        \newcommand\robotMovement[3]{
            \begin{scope}[shift={#1},rotate=#2,black!30]
                \ifthenelse{\equal{#3}{2}}{\draw[black,thick]}{\draw} (0,0) arc (0:45:1cm);
                \ifthenelse{\equal{#3}{1}}{\draw[black,thick]}{\draw} (0,0) -- (0,1cm);
                \ifthenelse{\equal{#3}{3}}{\draw[black,thick]}{\draw} (0,0) arc (0:-45:-1cm);
            \filldraw (0,0) circle (0.1cm);
            \end{scope}
        }
        % The whole map
        \draw (0,0) rectangle (5cm,5cm);
        \clip (0,0) rectangle (5cm,5cm);

        % Obstacles
        \draw[fill=black] (3cm,2cm) rectangle (5cm,2.5cm);
        \draw[fill=black] (3cm,2.5cm) rectangle (3.5cm,4cm);

        % Robot C-space coordinates
        \coordinate (path00) at (4.6cm,0.6cm);

        \coordinate (path0f) at ($(path00)+(0,1cm)$);
        \coordinate (path01) at ($(path00)!   1!   45:(path0f)$);
        \coordinate (path02) at ($(path00)!0.79! 22.5:(path01)$);
        \coordinate (path03) at ($(path00)!0.79!-22.5:(path01)$);

        \coordinate (path1f) at ($(path02)+(0,1cm)$);
        \coordinate (path11) at ($(path02)!   1!   90:(path1f)$);
        \coordinate (path12) at ($(path02)!0.79! 22.5:(path11)$);
        \coordinate (path13) at ($(path02)!0.79!-22.5:(path11)$);

        \coordinate (path2f) at ($(path13)+(0,1cm)$);
        \coordinate (path21) at ($(path13)!   1!   45:(path2f)$);
        \coordinate (path22) at ($(path13)!0.79! 22.5:(path21)$);
        \coordinate (path23) at ($(path13)!0.79!-22.5:(path21)$);

        \coordinate (path3f) at ($(path23)+(0,1cm)$);
        \coordinate (path31) at ($(path23)!   1!   45:(path3f)$);
        \coordinate (path32) at ($(path23)!0.79! 22.5:(path31)$);
        \coordinate (path33) at ($(path23)!0.79!-22.5:(path31)$);

        \coordinate (path4f) at ($(path31)+(0,1cm)$);
        \coordinate (path41) at ($(path31)!   1!    0:(path4f)$);
        \coordinate (path42) at ($(path31)!0.79! 22.5:(path41)$);
        \coordinate (path43) at ($(path31)!0.79!-22.5:(path41)$);

        \coordinate (path5f) at ($(path41)+(0,1cm)$);
        \coordinate (path51) at ($(path41)!   1!    0:(path5f)$);
        \coordinate (path52) at ($(path41)!0.79! 22.5:(path51)$);
        \coordinate (path53) at ($(path41)!0.79!-22.5:(path51)$);

        \coordinate (path6f) at ($(path53)+(0,1cm)$);
        \coordinate (path61) at ($(path53)!   1!  -45:(path6f)$);
        \coordinate (path62) at ($(path53)!0.79! 22.5:(path61)$);
        \coordinate (path63) at ($(path53)!0.79!-22.5:(path61)$);

        \coordinate (path7f) at ($(path63)+(0,1cm)$);
        \coordinate (path71) at ($(path63)!   1!  -90:(path7f)$);
        \coordinate (path72) at ($(path63)!0.79! 22.5:(path71)$);
        \coordinate (path73) at ($(path63)!0.79!-22.5:(path71)$);

        \coordinate (path8f) at ($(path73)+(0,1cm)$);
        \coordinate (path81) at ($(path73)!   1! -135:(path8f)$);
        \coordinate (path82) at ($(path73)!0.79! 22.5:(path81)$);
        \coordinate (path83) at ($(path73)!0.79!-22.5:(path81)$);

        \coordinate (path9f) at ($(path83)+(0,1cm)$);
        \coordinate (path91) at ($(path83)!   1! -135:(path9f)$);
        \coordinate (path92) at ($(path83)!0.79! 22.5:(path91)$);
        \coordinate (path93) at ($(path83)!0.79!-22.5:(path91)$);

        \robotMovement{(path00)}{45}{2}
        \robotMovement{(path02)}{90}{3}
        \robotMovement{(path13)}{45}{1}
        \robotMovement{(path21)}{45}{3}
        \robotMovement{(path31)}{0}{1}
        \robotMovement{(path41)}{0}{3}
        \robotMovement{(path53)}{-45}{3}
        \robotMovement{(path63)}{-90}{3}
        \robotMovement{(path73)}{-135}{3}
        \robotMovement{(path83)}{-180}{2}
        \filldraw[black!70] (path93) circle (0.1cm);

        % Robot
        \begin{scope}[shift={(4.6cm,0.6cm)},rotate=45,scale=0.5]
        % Body
        \draw[fill=white] (-3mm,-3mm) rectangle (3mm,3mm);
        % Wheels
        \draw[fill=black] (-4mm,0.75mm) rectangle (-3mm,2.25mm);
        \draw[fill=black] (3mm,0.75mm) rectangle (4mm,2.25mm);
        \draw[fill=black] (-4mm,-0.75mm) rectangle (-3mm,-2.25mm);
        \draw[fill=black] (3mm,-0.75mm) rectangle (4mm,-2.25mm);
        \end{scope}
    \end{tikzpicture}
    \end{document}

which generates this: cart

6

This is very particular to this special case but it is pretty much generalizable to more path cases. The idea would be essentially the same.

I wrote a simple decoration for the three-way path alternatives and made it drawn under the original path. Then drew the path itself and placed a dot decoration over it. Many parameters can be moved to keys and set independently but that's up to your convenience and use case.

For the use of it, it needs two parameters. First parameter is the multiple of 45 for the starting orientation and second is the array of path choices. I've counted from one to three from left turn,straight, right turn. For example,

\robotrace{5}{3,1,2,1} 

starts from facing south west and turns right, left, straight and then left again. You can also put this command inside a scope and shift it around for changing the initial point.

So the whole code is;

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{decorations}
\pgfdeclaredecoration{xroad}{junction}{%
\state{junction}[width=\pgfdecoratedinputsegmentlength]{%
\pgfpathmoveto{\pgfpointorigin}\pgfpathlineto{\pgfpointpolar{0}{1cm}}%
\pgfpathmoveto{\pgfpointorigin}\pgfpatharc{90}{45}{1cm}%
\pgfpathmoveto{\pgfpointorigin}\pgfpatharc{90}{135}{-1cm}}%
}
\pgfdeclaredecoration{xpost}{dot}{%
\state{dot}[width=\pgfdecoratedinputsegmentlength]{%
\pgfsetfillcolor{gray!50}
\pgfpathcircle{\pgfpointorigin}{1mm}\pgfusepath{fill}}%
}

\def\robotrace#1#2{
\def\mytotalrot{#1}
\draw[preaction={decorate,decoration={xroad},draw,very thin,gray},
      postaction={decoration=xpost,decorate}]
(0,0) \foreach\x in {#2}{
\ifnum\x>1\ifnum\x>2%
    arc (90+\mytotalrot*45:{90+(\mytotalrot-1)*45}:1cm)
  \else
    --++(\mytotalrot*45:1cm)
  \fi\else
    arc (-90+\mytotalrot*45:{-90+(\mytotalrot+1)*45}:1cm)
\fi
\pgfextra{\pgfmathparse{\mytotalrot+2-\x}\xdef\mytotalrot{\pgfmathresult}}}
    node[circle,fill=black,inner sep=0,minimum size=2mm]{};
}

\begin{document}
\begin{tikzpicture}
\draw[style=help lines] (0,0) grid[step=1cm] (5,5);
\robotrace{1}{2,3,1,1,1,3,3,2,3,3,3,2,2,3,1,1}
\end{tikzpicture}
\end{document}

enter image description here

  • dang, I forgot the robot itself. – percusse Aug 2 '14 at 7:34
  • Amazing! Great solution. – Javier C. Aug 2 '14 at 14:29
2

I think this does it.

\documentclass[border=5]{standalone}
\usepackage{tikz} 
\usetikzlibrary{decorations.pathreplacing}
\tikzset{%
  robot/.style={
     %robot path,
     postaction={decoration={robot}, decorate},
     robot movements/.cd, #1
  },
  robot swoosh/.style={draw=gray},
  robot movements/.cd,
  .unknown/.code={
    \let\robotkey\pgfkeyscurrentname% 
    \pgfkeys{/tikz/\robotkey/.try={#1}}%
  },
  robot step/.store in=\robotstep,
  robot step=3cm,
  robot swoosh factor/.store in=\robotswooshfactor,
  robot swoosh factor=1.25,
  home/.style={ insert path={ (0,0)  } },
  forward/.style={ 
    insert path={ [shift=(90:\robotstep)] -- (0,0) } 
  },
  left/.style={ 
    insert path={ arc (0:45:\robotstep*sqrt 2)
    [shift=(112.5:2*\robotstep*sqrt 2*sin 22.5),rotate=45] } 
  },
  right/.style={ 
    insert path={  arc (180:135:\robotstep*sqrt 2)
    [shift=(90-22.5:2*\robotstep*sqrt 2*sin 22.5),rotate=-45] } 
  },
  turn left/.style={ rotate=45 },
  turn right/.style={ rotate=-45 },
  stop/.style={  insert path={ (0,0)  } }
}
\def\curvetotext{curveto}

\pgfdeclaredecoration{robot}{start}{
\state{start}[next state=draw, width=0pt]{
\fill [black!75] circle [radius=0.25];}
\state{draw}[width=\pgfdecoratedinputsegmentlength,
switch if less than=\pgfdecoratedinputsegmentlength+1pt to final]{%
  \dorobotswoosh
  \fill [black!50] circle [radius=0.25];
}
\state{final}{
  \dorobotswoosh 
  \dorobot
}
}

\def\getrobotangle{%
\pgfmathparse{\pgfdecoratedinputsegmentstartangle-\pgfdecoratedinputsegmentendangle}
\pgfmathparse{int(round(\pgfmathresult/45)*45)}%
\pgfmathparse{\pgfmathresult >180 ? \pgfmathresult-360 : \pgfmathresult}
\pgfmathparse{\pgfmathresult <-180 ? 360+\pgfmathresult : \pgfmathresult}
\let\robotangle=\pgfmathresult
\pgftransformshift{\pgfpointdecoratedinputsegmentlast}%
\pgftransformresetnontranslations
\pgftransformrotate{\pgfdecoratedinputsegmentendangle}%
}
\def\dorobotswoosh{%
  \getrobotangle%
  \ifx\pgfdecorationcurrentinputsegment\curvetotext
  \draw [robot swoosh/.try] (0,0) -- (180:\robotstep/2*\robotswooshfactor) [yscale=\robotangle/45] 
   (0,0) arc (270:225:\robotstep*\robotswooshfactor/sqrt 2 and \robotstep/2);
  \else
    \draw [robot swoosh/.try] 
   (0,0) arc (270:225:\robotstep*\robotswooshfactor/sqrt 2 and \robotstep/2)
   [yscale=-1] 
   (0,0) arc (270:225:\robotstep*\robotswooshfactor/sqrt 2 and \robotstep/2);
  \fi
}%
\def\dorobot{
 \filldraw [fill=white, draw=black]
     (-0.5,-0.5) rectangle ++(1,1);
 \fill (-0.375,0.375) rectangle ++(0.25,0.25)
 (0.375,0.375) rectangle ++(-0.25,0.25)
 (-0.375,-0.375) rectangle ++(0.25,-0.25)
 (0.375,-0.375) rectangle ++(-0.25,-0.25);
}

\begin{document}
\begin{tikzpicture}
\draw [robot={home, turn left, right, left, left, left, left, 
  forward, left, forward, left, right, stop}];

\end{tikzpicture} 
\end{document}

enter image description here

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.