How can I do an affine coordinate transformation in pgfplots?

It is possible to perform a general affine coordinate transformation in TikZ by combining the shift, x, and y keys (or just the generalized cm key to set the coordinate matrix and shift). This allows one to write natural-looking code to draw, for example, a shifted, slanted, skewed parabola:

\tikzset{shift = {(2,3)}, x = {(1,-2)}, y = {(5,1)}}
\draw plot (\x, \x^2);


I don't use TikZ to plot functions, because drawing the axes and handling scaling is a pain; I use pgfplots. Unfortunately, because of the way that package implements the solution to the scaling problem, there is no direct connection between its coordinates and those of the surrounding TikZ picture, which means that these same keys do not have the desired effect when passed inside an axis.

Now, I know that there is such a thing as axis cs and axis direction cs, and I also know what the section on "TikZ interoperability" says, but neither does what I want. The former works if I'm drawing a TikZ picture, and the latter makes me responsible for the size of the axes. It does not appear that using, say, shift = {(axis direction cs:2,3)} actually places the computational origin (as compared to the axis origin, which had better stay at the original (0,0)!) at the axis coordinates (2,3) (nor does any of the other two obvious alternative coordinate systems). I want the following:

• A way to make a local change of coordinates, inside a scope, that shifts plots given via \addplot according to an affine transformation.

• pgfplots transparency in this change: I want to be able to use its scaled coordinates obliviously.

It would be wonderful if there were affine transformations among the options available to data cs but it only does polar. I am not enthusiastic about figuring out how to use \pgfplotsdefinecstransform just to make it do this simple thing, either.

Here is an example that should work with such a solution:

\documentclass{article}
\usepackage{pgfplots}
\begin{document}

\begin{tikzpicture}\begin{axis}

% What I want to write
\begin{scope}[ % Equivalent of:
% shift = {(0.5,0.5)},
% x = {(1,-1)},
% y = {(2, 1)},
%% Other options, such as:
/pgfplots/every axis plot/.append style = {very thick}
]
\end{scope}

% What I now have to write
\begin{scope}[
/pgfplots/every axis plot/.append style = {very thick}
]
\addplot ({0.5 + 0.5*exp(x) + 0.75*2*exp(-x)},
{0.5 + 0.5*-1*exp(x) + 0.75*exp(-x)});
\end{scope}

\end{axis}\end{tikzpicture}

\end{document}


I think you can see why I'd want this. (For the curious, I'm drawing trajectories of some solutions to systems of linear ODEs.)

-
Good question. Unfortunately, the code-keys x coord trafo and y coord trafo only accept one argument, x and y respectively. You cannot transform a x coordinate based on the x and the y value. Maybe the best solution is to implement a \pgfplotsdefinecstransform that uses three key-values (i.e. a shift, x and y). –  Qrrbrbirlbel Apr 1 at 3:20
Ooh, I forgot to dismiss those keys :) Yes, I thought of that, and found the same problem. Really, my only hope for this question is that there is some magic invocation of the tikz options that works, or that I can bribe someone into writing the cs transform for me :) –  Ryan Reich Apr 1 at 3:29
It's relatively easy to do it with tables if you are willing to create some temporary data points but for function expressions I guess you have to bite the bullet and define the transformations. –  percusse Apr 1 at 3:41

Pgfplots has a couple of differents coordinate mappings. What you need here is filter point: it accepts /data point/x and /data point/y on input and may modify each of its input values.

Your example could become:

\documentclass{article}
\usepackage{pgfplots}
\begin{document}
\thispagestyle{empty}

\begin{tikzpicture}\begin{axis}

% What I want to write
\begin{scope}[ % Equivalent of:
% shift = {(0.5,0.5)},
% x = {(1,-1)},
% y = {(2, 1)},
%% Other options, such as:
/pgfplots/every axis plot/.append style = {very thick},
/pgfplots/filter point/.code={%
\pgfkeysgetvalue{/data point/x}\X
\pgfkeysgetvalue{/data point/y}\Y
%
\def\eXX{1}%
\def\eXY{-1}%
%
\def\eYX{2}%
\def\eYY{1}%
%
\def\sX{0.5}%
\def\sY{0.5}%
%
\pgfmathparse{\X * \eXX + \Y * \eYX + \sX}%
\let\outX=\pgfmathresult
%
\pgfmathparse{\X * \eXY + \Y * \eYY + \sY}%
\let\outY=\pgfmathresult
%
\pgfkeyslet{/data point/x}\outX
\pgfkeyslet{/data point/y}\outY
},
]
\end{scope}
\end{axis}\end{tikzpicture}

\begin{tikzpicture}\begin{axis}

% What I now have to write
\begin{scope}[
/pgfplots/every axis plot/.append style = {very thick}
]
\addplot ({0.5 + 0.5*exp(x) + 0.75*2*exp(-x)},
{0.5 + 0.5*-1*exp(x) + 0.75*exp(-x)});
\end{scope}

\end{axis}\end{tikzpicture}

\end{document}


I realized that the different coordinate transformations have good reference in the reference manual, but their relation and order is undocumented. I added a section "Interaction of Transformations" (will become available as manual for the pgfplots unstable on http://pgfplots.sourceforge.net/ in the next days).

-
I'll try this out later but it looks good! I considered filtering but missed the one that filters the while point rather than each coordinate. (I am a big user of restrict y to domain.) –  Ryan Reich Apr 1 at 13:09
Thanks again for your great solution! –  Ryan Reich Apr 4 at 23:16

To complement Christian's answer, here is what I'm actually doing. It's the same code, but a little more polished/reusable.

\def\pt(#1,#2)#3{%
\expandafter\def\csname#3X\endcsname{#1}
\expandafter\def\csname#3Y\endcsname{#2}
}%
\pgfplotsset{
% x-vec, y-vec, shift
affine cs/.style n args = {3}{
/pgfplots/filter point/.code = {
\pgfkeysgetvalue{/data point/x}\X
\pgfkeysgetvalue{/data point/y}\Y
\pt#1{xvec}
\pt#2{yvec}
\pt#3{shift}
\pgfmathparse{\xvecX*\X+\yvecX*\Y+\shiftX}
\pgfkeyslet{/data point/x}\pgfmathresult
\pgfmathparse{\xvecY*\X+\yvecY*\Y+\shiftY}
\pgfkeyslet{/data point/y}\pgfmathresult
}
},
}


Possibly my choice of name for \pt is unfortunate. It does have the salutary effect of creating a long line of \p's in the code :) One can then use the key via

/pgfplots/affine cs = {(x-vec)}{(y-vec)}{(shift-vec)}


in an axis or a scope.

-