我想定义一个命令,用 pgf/tikz 绘制一些特殊的折线。在这些折线中,线条样式是交替变化的。以下是我的 latex 代码:
\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}
输出结果如下:
现在我想在折线末端添加一个箭头。我该如何修改上述命令定义?
答案1
您想要做的是仅在到达最后一个元素时绘制箭头。不幸的是,正如在如何获取 \foreach 循环中元素的数量?,\foreach
没有内置机制先验确定传递给它的元素总数;因此,它无法检测到在处理时已经到达最后一个元素。
这里有两种替代方法。
1 - 反向绘制路径
反转您传递的列表\foreach
(使终点成为起点)并仅在“开始处”绘制箭头(当\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 - 预先计算元素总数
首先计算元素的数量,然后在循环主体中设置一个测试\foreach
来检测是否已到达最后一个元素,并且仅在那种情况下绘制箭头。
\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}
答案2
可能有点过度,而且效率可能不如使用迭代点列表那么高\foreach
,但以下polyline
装饰(我认为)几乎完全概括了任意数量风格的要求。
.1pt
请注意,如果路径长度不足,则可能会失败。
\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}