我希望能够用它pgfplots
来展示滑雪者从滑雪坡上滑下来的情景。以下是我目前所做的工作。
\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}
我如何使用散射源信息来旋转标记,以便滑雪者的滑雪板与斜坡平行(稍微移动一下也会很好,因为我犯了一个设计错误,没有定义滑雪板的原点...)?
注意:我ski
也可以提供一个形状。
\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}
%
}
}
答案1
不幸的是,我认为没有简单的方法来旋转情节标记。你可能需要打开一个功能要求为了这。
解决此问题的一种方法是使用scatter/@pre marker code
对函数进行数值微分,并相应地旋转标记。要使此方法有效,您需要设置disabledatascaling
或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}
或者,您可以使用以下代码的改编版本:pgfplots - 将节点放置在绘图的 x 坐标上。目前它非常笨重,主要是因为您需要调整函数meta
以便它生成 0:1000 范围内的数字。
但至少它有效......
\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}
答案2
请注意,由于使用了节点而不是标记,因此以下内容解决了问题但没有回答所提出的问题。
更新答案 (感谢@Jake 的评论和调试)
这里的关键思想是使用/pgfplots/scatter/@pre marker code/.append code
。
首先,一些代码用于获取函数导数的值(通过密钥给出 scatter src
,是的,需要明确给出导数),该值以\pgfplotsmeta
数字形式存储fpu
,因此必须先进行转换。结果是切线的斜率。
\pgfkeys{/pgf/fpu,pgf/fpu/output format=fixed}
\pgfmathparse{\pgfplotspointmeta}%
\pgfkeys{/pgf/fpu = false}
\edef\myslope{\pgfmathresult}%
然后\path
必须将 设置为transparent
,否则绘制的 会给出轴坐标系中切线的实际方向,这要归功于技巧axis direction cs
。原点(0,0)
与标记混淆。
\path[transparent] (0,0) -- node[opaque,sloped,at
start,draw,shape=ski,anchor=bottom leg] {} ++(axis
direction cs:1,\myslope);
现在,完整的代码。
\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}
第一个答案 (并非所有功能都适用,请参阅评论)
我找到了一种方法来做到这一点。正如@Jake所说,我不能依赖一阶导数的值来给出切线的角度,因为轴没有相同的单位(一般来说)。所以这个想法是使用技巧axis cs:
来访问坐标系(然后访问轴的 x 和 y 单位)。最后,我没有使用任何标记,滑雪者被绘制为一个节点。幸运的是,绘制命令从标记中继承了所有图形参数(包括元颜色信息)。
\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}