# Can I rotate a marker with a scatter source?

I'd like to be able to use pgfplots to show a skier going down a ski slope. Here is what I have done so far.

\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.7}
\makeatletter
\pgfkeyssetvalue{/pgf/shapes/ski/ski length}{1cm}
\pgfkeyssetvalue{/pgf/shapes/ski/ski radius}{.075cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg length}{.3cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg angle}{140}
\pgfkeyssetvalue{/pgf/shapes/ski/back length}{.5cm}
\pgfkeyssetvalue{/pgf/shapes/ski/head radius}{.1cm}
\pgfdeclareplotmark{skimark}{%
% ski
\pgfpathmoveto{\pgfpoint{-.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpoint{.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpatharc{-90}{0}{\pgfkeysvalueof{/pgf/shapes/ski/ski radius}}
\pgfsetlinewidth{.5mm}
\pgfusepath{stroke}
% body
\pgfpathmoveto{%
\pgfpoint{+0pt}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpointorigin}
\pgfpathlineto{%
\pgfpointpolar{\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}
\pgfsetroundjoin
\pgfsetlinewidth{1mm}
\pgfusepath{stroke}
%
\pgfpathcircle{%
\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}{%
\pgfkeysvalueof{/pgf/shapes/ski/head radius}}
\pgfusepath{fill}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\begin{axis}[domain = 0:5,
]
\addplot+[mark = skimark,
only marks,
forget plot,
scatter,
scatter src = {-exp(-x)}] gnuplot[samples=5] {exp(-x)};
\addplot+[no markers] gnuplot[samples=30] {exp(-x)};
\end{axis}
\end{tikzpicture}
\end{document}


How can I use the scatter source information to rotate the markers so that the skier's skis are parallel to the slope (a bit of shifting would be good as well since I made a design error not defining the origin on the skis...) ?

Note: I can provide a ski shape as well.

\pgfdeclareshape{ski}{%
\anchor{center}{\pgfpointorigin}
\savedanchor\bottomleg{%
\pgfpoint{+0pt}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
%
\backgroundpath{%
% ski
\pgfpathmoveto{\pgfpoint{-.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpoint{.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpatharc{-90}{0}{\pgfkeysvalueof{/pgf/shapes/ski/ski radius}}
\pgfsetlinewidth{.5mm}
\pgfusepath{stroke}
% body
\pgfpathmoveto{\pgf@process{\bottomleg}}
\pgfpathlineto{\pgfpointorigin}
\pgfpathlineto{\pgfpointpolar{\pgfkeysvalueof{/pgf/shapes/ski/leg
angle}}{\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}
\pgfsetroundjoin
\pgfsetlinewidth{1mm}
\pgfusepath{stroke}
%
\pgfpathcircle{%
\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}{%
\pgfkeysvalueof{/pgf/shapes/ski/head radius}}
\pgfusepath{fill}
%
}
}

-

## 2 Answers

Unfortunately, I don't think there's an easy way to rotate the plot markers. You might want to open a feature request for this.

One way to work around this is to use scatter/@pre marker code to numerically differentiate your function and rotate the marks accordingly. For this to work, you either need to set disabledatascaling or axis equal.

\documentclass[border=5mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.7}
\makeatletter
\pgfkeyssetvalue{/pgf/shapes/ski/ski length}{1cm}
\pgfkeyssetvalue{/pgf/shapes/ski/ski radius}{.075cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg length}{.3cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg angle}{140}
\pgfkeyssetvalue{/pgf/shapes/ski/back length}{.5cm}
\pgfkeyssetvalue{/pgf/shapes/ski/head radius}{.1cm}
\pgfdeclareplotmark{skimark}{%
% ski
\pgfpathmoveto{\pgfpoint{-.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpoint{.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpatharc{-90}{0}{\pgfkeysvalueof{/pgf/shapes/ski/ski radius}}
\pgfsetlinewidth{.5mm}
\pgfusepath{stroke}
% body
\pgfpathmoveto{%
\pgfpoint{+0pt}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpointorigin}
\pgfpathlineto{%
\pgfpointpolar{\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}
\pgfsetroundjoin
\pgfsetlinewidth{1mm}
\pgfusepath{stroke}
%
\pgfpathcircle{%
\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}{%
\pgfkeysvalueof{/pgf/shapes/ski/head radius}}
\pgfusepath{fill}
}
\makeatother

\begin{document}

\newcommand{\calcangle}[2]{
\def\x{#2-0.01}
\pgfmathsetmacro\fxone{#1}
\def\x{#2+0.01}
\pgfmathsetmacro\fxtwo{#1}
\pgfmathsetmacro\test{atan2(1,(\fxone-\fxtwo)/0.02*\pgfplotsunitylength/\pgfplotsunitxlength)}
}

\begin{tikzpicture}
\begin{axis}[
domain = 0:4,
xmax=4,
disabledatascaling,
enlargelimits=true,
]
\addplot+[mark = skimark,
forget plot,
scatter,
mark phase=2, mark repeat=6,
visualization depends on=x \as \rawx,
scatter/@pre marker code/.append code={
\calcangle{exp(-\x)}{\rawx}
\pgftransformrotate{\test}
\pgftransformyshift{1.1*\pgfkeysvalueof{/pgf/shapes/ski/leg length}}
},]{exp(-x)};
\end{axis}
\end{tikzpicture}
\end{document}


Alternatively, you could use an adapted version of the code given at pgfplots - Placing Nodes on x Coordinates of a Plot. It's very clunky at the moment, mainly in the way that you need to adapt the meta function so that it generates numbers in the range 0:1000.

But at least it works...

\documentclass[border=5mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.7}
\usetikzlibrary{intersections}

\makeatletter
\pgfkeyssetvalue{/pgf/shapes/ski/ski length}{1cm}
\pgfkeyssetvalue{/pgf/shapes/ski/ski radius}{.075cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg length}{.3cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg angle}{140}
\pgfkeyssetvalue{/pgf/shapes/ski/back length}{.5cm}
\pgfkeyssetvalue{/pgf/shapes/ski/head radius}{.1cm}
\pgfdeclareshape{ski}{%
\anchor{center}{\pgfpointorigin}
\savedanchor\bottomleg{%
\pgfpoint{+0pt}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
%
\backgroundpath{%
% ski
\pgfpathmoveto{\pgfpoint{-.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpoint{.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpatharc{-90}{0}{\pgfkeysvalueof{/pgf/shapes/ski/ski radius}}
\pgfsetlinewidth{.5mm}
\pgfusepath{stroke}
% body
\pgfpathmoveto{\pgf@process{\bottomleg}}
\pgfpathlineto{\pgfpointorigin}
\pgfpathlineto{\pgfpointpolar{\pgfkeysvalueof{/pgf/shapes/ski/leg
angle}}{\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}
\pgfsetroundjoin
\pgfsetlinewidth{1mm}
\pgfusepath{stroke}
%
\pgfpathcircle{%
\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}{%
\pgfkeysvalueof{/pgf/shapes/ski/head radius}}
\pgfusepath{fill}
%
}
}

\tikzset{
add skier at x/.style={
name path global=plot line,
/pgfplots/execute at end plot visualization/.append={
\begingroup
\path [name path global = position line #1-1]
({axis cs:#1,0}|-{rel axis cs:0,0}) --
({axis cs:#1,0}|-{rel axis cs:0,1});
\path [xshift=1pt, name path global = position line #1-2]
({axis cs:#1,0}|-{rel axis cs:0,0}) --
({axis cs:#1,0}|-{rel axis cs:0,1});
\path [
name intersections={
of={plot line and position line #1-1},
name=left intersection
},
name intersections={
of={plot line and position line #1-2},
name=right intersection
},
label node/.append style={pos=1}
] (left intersection-1) -- (right intersection-1);
\pgfmathparse{(-exp(-#1)+1)*1000} % This has to be changed according to the function generating the color value
\pgfplotscolormapaccess[0:1000][1]{\pgfmathresult}{\pgfkeysvalueof{/pgfplots/colormap name}}
\definecolor{mapped color}{rgb}{\pgfmathresult}
\pgftransformarrow{\pgfpointanchor{left intersection-1}{center}}{\pgfpointanchor{right intersection-1}{center}}
\node [ski, transform shape, yshift=1.1*\pgfkeysvalueof{/pgf/shapes/ski/leg length}, mapped color] {};
\endgroup
}
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\begin{axis}[domain = 0:5,
]
\addplot[
add skier at x=0.1,
add skier at x=0.9,
add skier at x=2.5,
add skier at x=4
] {exp(-x)};

\end{axis}
\end{tikzpicture}
\end{document}

-
Thanks Jake: as you say, it works! –  cjorssen Jan 16 '13 at 14:07
BTW I found something with the @pre marker code key that nearly works (I must be missing something obvious): /pgfplots/scatter/@pre marker code/.append code = {\pgfkeys{/pgf/fpu,pgf/fpu/output format=fixed} \pgfmathparse{atan(\pgfplotspointmeta)} \pgfkeys{/pgf/fpu = false} \pgftransformrotate{\pgfmathresult}} –  cjorssen Jan 16 '13 at 14:08
@cjorssen: That only works if you set axis equal, and only if your function is monotonically decreasing. –  Jake Jan 16 '13 at 14:47
@cjorssen: I've edited my answer to include a more elegant approach, based on @pre marker code as you suggested. For this to work we need to know the unit vectors of the axis, so either axis equal has to be set or you can use disabledatascaling, which makes \pgfplotsunitxlength and \pgfplotsunitylength available. –  Jake Jan 16 '13 at 15:44

Note that the following solves the problem but does not answer the question as asked since a node is used instead of a marker.

Updated answer (thanks to @Jake comments and debugging)

The key idea here is to use /pgfplots/scatter/@pre marker code/.append code.

First some code to get the value of the derivative of the function (given via the scatter src key, yes one needs to give explicitly the derivative) stored in \pgfplotsmeta as a fpu number, so it has to be converted first. The result is the slope of the tangent.

\pgfkeys{/pgf/fpu,pgf/fpu/output format=fixed}
\pgfmathparse{\pgfplotspointmeta}%
\pgfkeys{/pgf/fpu = false}
\edef\myslope{\pgfmathresult}%


Then a \path that must be set to be transparent, otherwise it is drawn will give the actual direction of the tangent in the axis coordinate system thanks to the axis direction cs trick. The origin (0,0) is confounded with the marker.

\path[transparent] (0,0) -- node[opaque,sloped,at
start,draw,shape=ski,anchor=bottom leg] {} ++(axis
direction cs:1,\myslope);


Now, the complete code.

\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.7}
\makeatletter
\pgfkeyssetvalue{/pgf/shapes/ski/ski length}{1cm}
\pgfkeyssetvalue{/pgf/shapes/ski/ski radius}{.075cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg length}{.3cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg angle}{140}
\pgfkeyssetvalue{/pgf/shapes/ski/back length}{.5cm}
\pgfkeyssetvalue{/pgf/shapes/ski/head radius}{.1cm}

\pgfdeclareshape{ski}{%
\anchor{center}{\pgfpointorigin}
\savedanchor\bottomleg{%
\pgf@x=0pt
\pgf@y=-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}
\anchor{bottom leg}{\bottomleg}
%
\backgroundpath{%
% ski
\pgfpathmoveto{\pgfpoint{-.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpoint{.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpatharc{-90}{0}{\pgfkeysvalueof{/pgf/shapes/ski/ski radius}}
\pgfsetlinewidth{.5mm}
\pgfusepath{stroke}
% body
\pgfpathmoveto{\pgf@process{\bottomleg}}
\pgfpathlineto{\pgfpointorigin}
\pgfpathlineto{\pgfpointpolar{\pgfkeysvalueof{/pgf/shapes/ski/leg
angle}}{\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}
\pgfsetroundjoin
\pgfsetlinewidth{1mm}
\pgfusepath{stroke}
%
\pgfpathcircle{%
\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}{%
\pgfkeysvalueof{/pgf/shapes/ski/head radius}}
\pgfusepath{fill}
%
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\begin{axis}[domain = 0:5,
]
\addplot+[%
% no markers,
only marks,
forget plot,
scatter,
/pgfplots/scatter/@pre marker code/.append code={%
\pgfkeys{/pgf/fpu,pgf/fpu/output format=fixed}
\pgfmathparse{\pgfplotspointmeta}%
\pgfkeys{/pgf/fpu = false}
\edef\myslope{\pgfmathresult}%
\path[transparent] (0,0) -- node[opaque,sloped,at
start,draw,shape=ski,anchor=bottom leg] {} ++(axis
direction cs:1,\myslope);
},
scatter src = {cos(deg(x))}] gnuplot[samples=10] {sin(x)};
\addplot+[no markers] gnuplot[samples=30] {sin(x)};
\end{axis}
\end{tikzpicture}

\end{document}


First answer (not working with all functions, see comments)

I found a way to do it. As @Jake said, I cannot rely on the value of the first derivative to give the angle of the tangent because the axis do not have the same unit (in general). So the idea is to use the axis cs: trick to access the coordinate system (and then access to the x and y unit of the axis). Finally, I do not use any mark and the skier is drawn as a node. Luckily, the draw command inherit all graphic parameters from the marker (including the meta color information).

\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.7}
\makeatletter
\pgfkeyssetvalue{/pgf/shapes/ski/ski length}{1cm}
\pgfkeyssetvalue{/pgf/shapes/ski/ski radius}{.075cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg length}{.3cm}
\pgfkeyssetvalue{/pgf/shapes/ski/leg angle}{140}
\pgfkeyssetvalue{/pgf/shapes/ski/back length}{.5cm}
\pgfkeyssetvalue{/pgf/shapes/ski/head radius}{.1cm}

\pgfdeclareshape{ski}{%
\anchor{center}{\pgfpointorigin}
\savedanchor\bottomleg{%
\pgf@x=0pt
\pgf@y=-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}
\anchor{bottom leg}{\bottomleg}
%
\backgroundpath{%
% ski
\pgfpathmoveto{\pgfpoint{-.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpoint{.5\pgfkeysvalueof{/pgf/shapes/ski/ski
length}}{-\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpatharc{-90}{0}{\pgfkeysvalueof{/pgf/shapes/ski/ski radius}}
\pgfsetlinewidth{.5mm}
\pgfusepath{stroke}
% body
\pgfpathmoveto{\pgf@process{\bottomleg}}
\pgfpathlineto{\pgfpointorigin}
\pgfpathlineto{\pgfpointpolar{\pgfkeysvalueof{/pgf/shapes/ski/leg
angle}}{\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}
\pgfpathlineto{\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}
\pgfsetroundjoin
\pgfsetlinewidth{1mm}
\pgfusepath{stroke}
%
\pgfpathcircle{%
\pgfpointadd{%
\pgfpointpolar{%
\pgfkeysvalueof{/pgf/shapes/ski/leg angle}}{%
\pgfkeysvalueof{/pgf/shapes/ski/leg length}}}{%
\pgfpoint{%
\pgfkeysvalueof{/pgf/shapes/ski/back length}}{+0pt}}}{%
\pgfkeysvalueof{/pgf/shapes/ski/head radius}}
\pgfusepath{fill}
%
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\begin{axis}[domain = 0:5,
]
\addplot+[no markers,
only marks,
forget plot,
scatter,
/pgfplots/scatter/@pre marker code/.append code={%
\pgfkeys{/pgf/fpu,pgf/fpu/output format=fixed}
\pgfmathparse{\pgfplotspointmeta}%
\pgfkeys{/pgf/fpu = false}
\edef\myslope{\pgfmathresult}%
\path[transparent] (axis cs:0,0) -- node[opaque,sloped,at
start,draw,shape=ski,anchor=bottom leg] {} (axis cs:1,\myslope);
},
scatter src = {-exp(-x)}] gnuplot[samples=5] {exp(-x)};
\addplot+[no markers] gnuplot[samples=30] {exp(-x)};
\end{axis}
\end{tikzpicture}
\end{document}

-
This only works for the exponential function, because the value of the function is equivalent to the value of its derivative. It won't work with any other function (try x, or sin(x) to see what I mean). –  Jake Jan 17 '13 at 13:19
@Jake You're right, I overlooked it: it does not work for sin(x) (the nodes are shifted above the plot,). It works for x or x^2 but not for x+1 or x^2+1 (nodes are shifted again). Hum, there must be a way to circumvent this. Some basic maths I guess... –  cjorssen Jan 17 '13 at 13:44
If you replace all occurrences of exp(-x) with x the skiers are correctly rotated for you? –  Jake Jan 17 '13 at 13:48
@Jake Yes, but I must also change the derivative of x to 1 in scatter src. –  cjorssen Jan 17 '13 at 13:49
Ah, okay, I missed that part! You can fix the problem of the shift by using \path[draw] (0,0) -- node[opaque,sloped,at start,draw,shape=ski,anchor=bottom leg] {} ++(axis direction cs:1,\myslope);, then it should work for all functions. –  Jake Jan 17 '13 at 13:59