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.

I am trying to draw a simple path using TikZ, with a custom syntax to specify it. This works:

\documentclass{article}
\usepackage{tikz}
\begin{document}

\newcommand{\latticepath}[1]{
    \draw[thick] (0,0) node{} \foreach \x in {#1}{ -- \if u\x ++(0,1) \else ++(1,0) \fi node{}};
}
\begin{tikzpicture}[x=3mm,y=3mm]
    \tikzset{every node/.style={circle,fill,draw=none,inner sep=2pt}}
    \latticepath{r,r,u,r,r,u,u,r,u,r,u,u}
\end{tikzpicture}

\end{document}

However, I would prefer not to comma-separate the values. So I wrote this command to convert a sequence of letters into path components:

\documentclass{article}
\usepackage{tikz}
\begin{document}

\makeatletter\let\mygobble\@gobble\makeatother
\newcommand{\readpath}[3]{
    \def\pathimpl##1{\ifx##1.\let\rest\mygobble\else\ifx##1u#1\else#2\fi\def\rest####1{####1}\fi\rest\pathimpl}
    \pathimpl#3.
}

\readpath{Up }{Right }{rrurruururuu}

However this implementation produces a bunch of errors due to the use of macros in paths:

\newcommand{\betterlatticepath}[1]{
    \draw[thick] (0,0) node{} \readpath{++(0,1) node{}}{++(1,0) node{}}{#1};
}
\begin{tikzpicture}
    \tikzset{every node/.style={circle,fill,draw=none,inner sep=2pt}}
    \betterlatticepath{rrurruururuu}
\end{tikzpicture}

\end{document}

Package tikz Error: Giving up on this path. Did you forget a semicolon?.
Undefined control sequence.
You can't use `macro parameter character #' in restricted horizontal mode.
Parameters must be numbered consecutively.
Parameters must be numbered consecutively.
Argument of \rest has an extra }.
Runaway argument?

I tried \pgfextra, but this doesn't help. How can I make this work?

share|improve this question
    
Please make your code compilable (if possible), or at least complete it with \documentclass{...}, the required \usepackage's, \begin{document}, and \end{document}. That may seem tedious to you, but think of the extra work it represents for TeX.SX users willing to give you a hand. Help them help you: remove that one hurdle between you and a solution to your problem. –  Jubobs Feb 9 at 12:07

2 Answers 2

up vote 7 down vote accepted

Here is a solution using \pgfextra, insert path and two custom keys:

enter image description here

\documentclass[tikz]{standalone}

\tikzset{
  recurse lattice path/.code args={#1#2}{
    \ifx#1.
    \else
    \ifx#1u
    \tikzset{insert path={-- ++(0,1) node[fill=black]{}}}
    \else
    \tikzset{insert path={-- ++(1,0) node[fill=black]{}}}
    \fi
    \tikzset{recurse lattice path=#2}
    \fi
  },
  lattice path/.code={%
    \draw (0,0) node[fill=black]{}
    \pgfextra{\tikzset{recurse lattice path={#1.}}};
  },
}

\begin{document}
\begin{tikzpicture}
  \tikzset{lattice path=rrurrur}
\end{tikzpicture}
\end{document}

Edit:

Here is a simpler solution without \pgfextra:

enter image description here

\documentclass[tikz]{standalone}

\tikzset{
  dep u/.style={insert path={-- ++(0,1) node{}}},
  dep r/.style={insert path={-- ++(1,0) node{}}},
  dep d/.style={insert path={-- ++(0,-1) node{}}},
  dep l/.style={insert path={-- ++(-1,0) node{}}},
  recurse lattice path/.code args={#1#2}{
    \ifx#1.\else\tikzset{dep #1,recurse lattice path=#2}\fi
  },
  lattice path/.style={recurse lattice path=#1.}
}

\begin{document}
\begin{tikzpicture}
  \tikzset{every node/.style={circle,fill,draw=none,inner sep=2pt}}
  \draw (0,0) node{} [lattice path=rrurrurrdddlll];
\end{tikzpicture}
\end{document}
share|improve this answer
    
Thanks, this is great! But I don't quite understand how the code args part works. Why does #1 get the first character while #2 gets the entire remaining path? –  jtbandes Feb 9 at 20:14
    
@jtbandes That's basically how TeX works with delimited macros. Try \tt \def\parse#1#2\eov{arg 1 -> (#1); arg 2 -> (#2);} \parse123456789\eov. It is basically what is happening inside \pgfkeys. –  Mark Wibrow Feb 10 at 7:12
    
@MarkWibrow That makes sense, I just didn't the code args setting is a delimited macro (since it just says #1#2 and not #1#2.). –  jtbandes Feb 11 at 4:07

Basically, you can't put a macro defined using \newcommand inside a TikZ path as it does a lot of stuff "under the covers" (e.g., \futurelet) which TikZ cannot handle.

You can, however, put arbitrary code inside \pgfextra{} in a TikZ path. In this case you could build the path inside \pgfextra and then insert it afterwards, like this:

\documentclass[tikz,border=0.125cm]{standalone}

\def\addtolatticepath#1{%
  \expandafter\def\expandafter\latticepath\expandafter{\latticepath#1}%
}

\def\latticepathletteru{\addtolatticepath{ -- ++(0,1) }}
\def\latticepathletterd{\addtolatticepath{ -- ++(0,-1) }}
\def\latticepathletterl{\addtolatticepath{ -- ++(-1,0) }}
\def\latticepathletterr{\addtolatticepath{ -- ++(1,0) }}

\def\parselatticepath#1{%
  \def\latticepath{node {}}%
  \Parselatticepath#1@}
\def\Parselatticepath#1{%
  \ifx#1@%
    \let\next=\relax%
  \else%
    \csname latticepathletter#1\endcsname%
    \addtolatticepath{ node {} }%
    \let\next=\Parselatticepath
  \fi%
  \next}

\tikzset{%
  insert lattice path/.style={%
    every node/.style={
      circle,
      fill,
      draw=none,
      inner sep=2pt
    },
    insert path={%
      \pgfextra{\parselatticepath{#1}}%
      \latticepath
    }
  }
}

\begin{document}
\begin{tikzpicture}
\draw (0,0) [insert lattice path={rrurruururuu}];
\end{tikzpicture}
\end{document}

enter image description here

share|improve this answer

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.