Why must the independent variable of a function to be plotted be a macro in TikZ?
When pgfmath
parser was written, the main aim was to provide consistent and slightly more versatile mathematical operations than the calc
package (which used to do all the calculations) without the overhead of the fp
package. Also the integration with \foreach
variables was important as well (as been suggested above).
So, every expression given to the pgfmath
parser is \edef
'ed immediately prior to parsing. This means that in most cases (unless you do something clever with \noexpand
inside double quotes) that
- any unexpanded tokens are
TeX
registers - the parser does not have to worry about checking whether tokens are expandable
As things stand it is possible to define a function which can stand for a plotting variable (as has been shown above). This is, to my knowledge, the only way of achieving the OPs requirements. But (as has also already been pointed out) parsing a number is quicker than parsing/evaluating a function.
As a "side note", I genuinely intended pgfmath
to be a temporary fix for mathematical operations inPGF
and thought that when luatex
came along "proper" programmers would jump in and sort things out. Sadly that hasn't happened.
Why? I don’t know.
Though, TikZ doesn’t do anything else but to iterate over the domain. When TikZ plots a function it uses the PGF macro \pgfplotfunction
for this. In our case it is called with something like (the third parameter is not correct but this is essentially how you would do this in PGF)
\pgfplotfunction{\t}{0, 0.10019, ..., 50}{\pgfpointpolar{\t r}{1+2*exp(-\t/10)}
In the definition of \pgfplotfunction
we find something like
% Initialize plotting
\foreach #1 in {#2}{
% parse #3, extract x and y and sent it to the plotter
}
% Do the actual plotting
Here’s a way around it by defining a PGFmath variable that simply expands to the variable used by the plotter. To not overwrite any already defined macros like \t
or \theta
(which could be used by a node on the path), I “hid” the actual variable name in a m@cro.
\tikzset{
variable*/.style={
declare function={#1=\tikz@plot@var;},
variable=\tikz@plot@var}}
You can now say variable*=theta
and then use theta
instead of \t
in the function.
I have added a “fast” version in the code below because our variable is already calculated by the \foreach
loop and doesn’t need additional parsing or evaluating. Thus, I copy the value of the iterator directly to \pgfmathresult
.
Note that if you plot with gnuplot (an external tool to TeX and TikZ) by using the function
plot operator as in
\draw plot[parametric, domain=0:50, samples=500, smooth]
function {cos(t)*(1+2*exp(-t/10)), sin(t)*(1+2*exp(-t/10))};
you need to use the variables x
and, for parametric plots, t
.
Code
\documentclass[border=5pt,tikz]{standalone}
\makeatletter
\tikzset{variable*/.code=\pgfmathdeclarefunction{#1}{0}{\let\pgfmathresult\tikz@plot@var}%
\def\tikz@plot@var{\tikz@plot@var}}
\makeatother
\begin{document}
\begin{tikzpicture}
\draw [domain=0:50,variable*=theta,smooth,samples=500]
plot (theta r: {1 + 2 * exp( -theta / 10)});
\end{tikzpicture}
\end{document}
(edit) 2017: since xint 1.1 (2014/10/28)
, xinttools
is not loaded by xintfrac
. Code updated.
Not that I recommend it at all, as the nice key=value syntax is lost, but it is possible to do a bit of the job done by TikZ
and avoid having a macro for the variable in plots. But one still needs a macro, now for the expression giving the plotted points. I tried without but it appears that after coordinates
one needs something completely expandable, so I could not use \xintFor
there.
The macros \Sample
, \SamplE
and \SampleFit
are a bit undecipherable, what they do is to compute the #1
which will be used by the macro \PointMacro
(name arbitrary) corresponding to the actual plot. The #1
will be a fixed point number with 4 digits after decimal mark.
\documentclass[border=5pt,tikz]{standalone}
\usepackage{xintfrac, xinttools}
% \Sample {N}\pointmacro {start:end}
% it returns expandably the N points from N equispaced samples starting with
% start and ending with end.
% \pointmacro (name arbitrary)
% should be a one-parameter macro which returns a point
% as recognized by tikz (such as (x,y), or (angle:radius))
% example \def\macro #1 {(#1, {(#1)^2})}
% see examples below
\def\Sample #1#2#3{\SamplE {#1}#2#3;}
\def\SamplE #1#2#3:#4;{\expandafter\xintApplyUnbraced\expandafter
{\expandafter\SampleFit\expandafter #2\expandafter
{\romannumeral0\xintdiv{\xintSub{#4}{#3}}{\xintDec{#1}}}{#3}}
{\xintSeq{0}{#1-1}}}
\def\SampleFit #1#2#3#4{\expandafter #1\expandafter{\romannumeral0%
\xinttrunc {4}{\xintAdd{#3}{\xintMul{#2}{#4}}}}}
\begin{document}
\tikzset {x=.5cm, y=.5cm}
\begin{tikzpicture}
\def\ParabolaPoint #1{(#1, {(#1)^2})}% negative #1 within parentheses!
\draw [smooth] plot coordinates {\Sample {25}\ParabolaPoint {-2:2}};
\end{tikzpicture}
\begin{tikzpicture}
\def\CubicPoint #1{(#1, {(#1)^3})}
\draw [smooth] plot coordinates {\Sample {25}\CubicPoint {-1.25:1.25}};
\end{tikzpicture}
\begin{tikzpicture}
\def\SpiralPoint #1{({#1 r}: {1+2*exp(-#1/10)})}
\draw [smooth] plot coordinates {\Sample {200}\SpiralPoint {0:50}};
\end{tikzpicture}
\end{document}