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 want to define a command for drawing some special polygonal lines with pgf/tikz. In these polygonal lines, the line styles are of alternating change. The following is my latex code:

\documentclass{standalone}
\usepackage{tikz}
\newcommand{\drawline}[4][]{
  \foreach \v [remember=\v as \u,count=\i] in {#4} {
    \ifnum \i > 1
      \ifodd \i \draw[#1,#3] \u -- \v; \else \draw[#1,#2] \u -- \v; \fi
    \fi
  }
}
\begin{document}
\begin{tikzpicture}
\drawline{solid,color=blue}{dashed,color=green}{(0,0),(1,1),(2,3),(5,5),(8,-1),(5,1)}
\end{tikzpicture}
\end{document}

The output figure is as follows:

enter image description here

Now I want to add an arrow at the end of the polygonal line. How could I modify the above command definiton for this?

enter image description here

share|improve this question
    
related: tex.stackexchange.com/questions/147544/… –  Jubobs May 4 at 11:00

2 Answers 2

up vote 6 down vote accepted

What you want to do is draw an arrowhead only when you reach the last element. Unfortunately, as discussed in How to get the number of elements in a \foreach loop?, \foreach has no built-in mechanism to a priori determine the total number of elements that are passed to it; therefore, it has no way of detecting that it has reached the last element at the time it processes it.

You have two alternative approaches, here.

enter image description here

1 - Draw the path in reverse

Reverse the list you pass to \foreach (make the end point the start point) and draw an arrowhead only "at the beginning" (when \i=2).

\documentclass{standalone}
\usepackage{tikz}

\makeatletter
\newcommand{\drawline}[4][]{
  \foreach \v [remember=\v as \u,count=\i] in {#4} {
    % draw an arrowhead only if we're processing the second element
    \ifnum \i = 2%
      \tikzset{tip/.style={<-}}%
    \else
      \tikzset{tip/.style={}}%
    \fi
    % the rest of your for loop
    \ifnum \i > 1%
      \ifodd \i%
        \draw[tip,#1,#3] \u -- \v;
      \else
        \draw[tip,#1,#2] \u -- \v;
      \fi
    \fi
  }%
}
\makeatother

\begin{document}
\begin{tikzpicture}
\drawline{solid,color=blue}{dashed,color=green}{(5,1),(8,-1),(5,5),(2,3),(1,1),(0,0)}
\end{tikzpicture}
\end{document}

2 - Count the total number of elements beforehand

Count the number of elements first, then set up a test in the body of your \foreach loop to detect whether the last element has been reached, and only draw an arrowhead in that case.

\documentclass{standalone}
\usepackage{tikz}

\newcount\foreachNumel

\makeatletter
\newcommand{\drawline}[4][]{
  % count the number of elements in #4
  \global\foreachNumel=0%
  \foreach \v in {#4}
    {\global\advance\foreachNumel by \@ne}
  \foreach \v [remember=\v as \u,count=\i] in {#4} {
    % draw an arrowhead only if we're processing the last element
    \ifnum \i = \foreachNumel%
      \tikzset{tip/.style={->}}%
    \else
      \tikzset{tip/.style={}}%
    \fi
    % the rest of your for loop
    \ifnum \i > 1%
      \ifodd \i%
        \draw[tip,#1,#3] \u -- \v;
      \else
        \draw[tip,#1,#2] \u -- \v;
      \fi
    \fi
  }%
}
\makeatother

\begin{document}
\begin{tikzpicture}
\drawline{solid,color=blue}{dashed,color=green}{(0,0),(1,1),(2,3),(5,5),(8,-1),(5,1)}
\end{tikzpicture}
\end{document}
share|improve this answer

Possibly overkill and arguably less efficient than iterating over a list of points using \foreach, but the following polyline decoration (I think) is an almost complete generalization of the requirements for an arbitrary number of styles.

Note it may fail with paths less than .1pt in length.

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{decorations}
\newcount\pgfdecoratedinputsegmentcount
\pgfdeclaredecoration{polyline}{start}{
\state{start}[width=0pt,
  next state=draw,
  persistent precomputation={\pgfdecoratedinputsegmentcount=1}]{}
\state{draw}[width=\pgfdecoratedinputsegmentlength,
  switch if less than=\pgfdecoratedinputsegmentlength+.1pt to final,
  persistent postcomputation={\advance\pgfdecoratedinputsegmentcount by1}]
{
  \pgfcoordinate{@1}{\pgfpointdecoratedinputsegmentfirst}%
  \pgfcoordinate{@2}{\pgfpointdecoratedinputsegmentlast}%
  \let\i=\pgfdecoratedinputsegmentcount%
  \pgfmathsetmacro\i{int(\pgfkeysvalueof{/pgf/decoration/polyline style function})}%
  \ifnum\pgfdecoratedinputsegmentcount=1\relax%
    \path [/pgf/decoration/every polyline/.try, 
      /pgf/decoration/polyline 1/.try, 
      /pgf/decoration/polyline first/.try]
      (@1) -- (@2) \pgfdecorationpolylinenodes;
  \else%
    \path [/pgf/decoration/every polyline/.try, 
      /pgf/decoration/polyline \i/.try]
      (@1) -- (@2) \pgfdecorationpolylinenodes;
  \fi%
}
\state{final}{
  \pgfcoordinate{@1}{\pgfpointdecoratedinputsegmentfirst}%
  \pgfcoordinate{@2}{\pgfpointdecoratedinputsegmentlast}%
  \let\i=\pgfdecoratedinputsegmentcount%
  \pgfmathsetmacro\i{int(\pgfkeysvalueof{/pgf/decoration/polyline style function})}%
  \path [/pgf/decoration/every polyline/.try, 
    /pgf/decoration/polyline \i/.try, 
    /pgf/decoration/polyline last/.try]
     (@1) -- (@2) \pgfdecorationpolylinenodes;
}
}
\pgfkeys{/pgf/decoration/.cd,
  polyline style function/.initial=\i}
\tikzset{%
  polyline nodes/.store in=\pgfdecorationpolylinenodes,
  polyline nodes=,
}
\begin{document}
\begin{tikzpicture}[polyline label/.style={
  circle, inner sep=1pt, font=\sffamily\footnotesize, fill=black!75, text=white,
}]
\path 
[decoration={polyline,
  polyline style function={mod(\i-1,7)+1},
  polyline 1/.style={draw=red},
  polyline 2/.style={draw=yellow},
  polyline 3/.style={draw=pink},
  polyline 4/.style={draw=green},
  polyline 5/.style={draw=orange},
  polyline 6/.style={draw=purple},
  polyline 7/.style={draw=blue},
  polyline last/.style={-stealth},
  every polyline/.style={ultra thick, 
    polyline nodes={node [midway, transform shape, polyline label] {\i}}
  }}, decorate]
  (0,0) \foreach \i in {1,...,28}{
    -- ++(rand*180-90:rnd*1+2)
  };

\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.