5

I am trying to find the intersections of a path with plots from pgfplots via the intersection library. The intersections found clearly do not match the visable intersections. It seems as if they are transformed to a different coordinate system. Is it possible to correctly calculate these intersections?

example

MWE

\documentclass{article}
\usepackage{pgfplots, tikz}
\usetikzlibrary{calc, intersections}    
\begin{document}
\begin{tikzpicture}

\begin{axis}[name=firstaxis]
\addplot[name path global=firstfunction]{e^x};
\end{axis}    
\begin{axis}[name=secondaxis, at={($(firstaxis.south)-(0,1cm)$)}, anchor=north]
\addplot[name path global=secondfunction]{x^3};
\end{axis}

% Draw intersection line 
\draw[color=black, opacity=1, name path global=intersectionline] (firstaxis.north) -- (secondaxis.south);

% Draw intersections points
\fill 
    [name intersections={of=intersectionline and firstfunction}] 
    (intersection-1) circle (2pt);          
\fill 
    [name intersections={of=intersectionline and secondfunction}] 
    (intersection-1) circle (2pt);

\end{tikzpicture}
\end{document}
7

I hope there is an easier way of doing this but...

Firstly, as I understand it PGFPlots calculates the points for the plot, puts the plotted path in an axis box and then positions the axis box in the picture. The shifting of the axis box to the required position does not change the coordinates of the underlying low level PGF path representation obtained using the name path keys from the intersection library.

You can see this if the saved plotted path is manually drawn (in red) directly after the second axis environment:

\documentclass[border=0.125cm]{standalone}
\usepackage{pgfplots}
\usetikzlibrary{calc,intersections}  

\tikzset{
    name path global/.append code={%
        \csname tikz@addmode\endcsname{%
            \pgfgetpath\tmp%
            \expandafter\global\expandafter\let\csname tikz@intersect@path@name@#1\endcsname=\tmp%
        }%
    }%
}  
\begin{document}


\begin{tikzpicture}


\begin{axis}
[
    name=firstaxis, 
    domain=-5:5, 
    xmin=-6, xmax=6, 
    ymin=-10, ymax=160, 
    enlargelimits=false
]
    \addplot[name path global=firstfunction]{e^x};

\end{axis}    

\begin{axis}
[
    name=secondaxis, 
    domain=-5:5,
    xmin=-6, xmax=6, 
    ymin=-150, ymax=150, 
    enlargelimits=false, 
    at={($(firstaxis.south)-(0,1cm)$)}, 
    anchor=north
]
    \addplot[name path global=secondfunction]{x^3};

\end{axis}

\expandafter\pgfsetpath\csname tikz@intersect@path@name@secondfunction\endcsname
\pgfsetstrokecolor{red}
\pgfusepath{stroke}

\end{tikzpicture}
\end{document}

enter image description here

The second problem is that the name path global key doesn't currently work properly with PGFPLots and has to hacked (as shown in the code above).

This means trying to do what you want is possible but a real nuisance. The most conceptually simple way would be to shift the underlying plotted path, but this would be time consuming for complex plots and involve a lot of low level stuff.

In the method shown below, coordinates of the intersectionline have to be shifted to the 'coordinate system' of any axis which is not positioned at the origin. Then the intersection points also have to be shifted as well.

This is (sort of easy) but then you also have to use enlargelimits=false (which acts like a kind of 'margin') and then manually set the limits for the |x| and |y| axes for both axes.

In addition the name path global key needs to be hacked a bit. It is a fair bit work, and I suspect some other kind of workaround not involving intersections would be much easier:

\documentclass[border=0.125cm]{standalone}
\usepackage{pgfplots}
\usetikzlibrary{calc,intersections}  

\tikzset{
    name path global/.append code={%
        \csname tikz@addmode\endcsname{%
            \pgfgetpath\tmp%
            \expandafter\global\expandafter\let\csname tikz@intersect@path@name@#1\endcsname=\tmp%
        }%
    }%
}  
\begin{document}


\begin{tikzpicture}


\begin{axis}
[
    name=firstaxis, 
    domain=-5:5, 
    xmin=-6, xmax=6, 
    ymin=-10, ymax=160, 
    enlargelimits=false
]
    \addplot[name path global=firstfunction]{e^x};

\end{axis}    

\begin{axis}
[
    name=secondaxis, 
    domain=-5:5,
    xmin=-6, xmax=6, 
    ymin=-150, ymax=150, 
    enlargelimits=false, 
    at={($(firstaxis.south)-(0,1cm)$)}, 
    anchor=north
]
    \addplot[name path global=secondfunction]{x^3};

\end{axis}


\foreach \col/\from/\to in {red/west/east, green/north/south, blue/north west/south east}{
    % Draw intersection line 
    \draw 
        [color=\col, opacity=1, name path global=intersectionline] 
        (firstaxis.\from) -- (secondaxis.\to);


    % Draw intersections points
    \fill 
        [\col, name intersections={of=intersectionline and firstfunction}] 
        (intersection-1) circle [radius=2pt];    

    % Create the intersectionline in the secondaxis coordinate system.
    \path [overlay] let \p1=(secondaxis.south west) in
        [name path global=intersectionline] 
        ([shift={(-\x1,-\y1)}]firstaxis.\from) -- ([shift={(-\x1,-\y1)}]secondaxis.\to);

    % Draw the intersection point
    \fill 
        [\col, name intersections={of=intersectionline and secondfunction}] 
        ($(secondaxis.south west)+(intersection-1)$) circle [radius=2pt];
}
\end{tikzpicture}
\end{document}

enter image description here

Ok, so it can be done the hard way as well. The macro \pgfpathretransform defined below 're-transforms' a low level PGF path. The result can be used using the transform named path key so that the intersection stuff is now less messy.

\documentclass[border=0.125cm]{standalone}
\usepackage{pgfplots}
\usetikzlibrary{calc,intersections}  
\makeatletter
\tikzset{
    name path global/.append code={%
        \tikz@addmode{%
            \pgfgetpath\tmp%
            \expandafter\global\expandafter\let\csname tikz@intersect@path@name@#1\endcsname=\tmp%
        }%
    },
    transform named path/.code args={#1 by #2}{
        \expandafter\let\expandafter\@tmp\csname tikz@intersect@path@name@#1\endcsname%
        \pgfpathretransform{\tikzset{#2}\tikz@transform}{\@tmp}{\@tmp}%
        \pgfinterruptpath%
            \path[name path=#1]\pgfextra{\pgfsetpath\@tmp};
        \endpgfinterruptpath%
    }
}  


% This macro 're-transforms' a soft-path
%
% #1 Pgf level transformation code
% #2 a macro containing the original soft path
% #3 a macro to store the transformed path
%

\def\pgfpathretransform#1#2#3{%
    % First get the transform...
    \begingroup%
        \pgftransformreset%
        #1%
        \pgfgettransform\pgfpath@retransform%
        \expandafter
    \endgroup%
    \expandafter\def\expandafter\pgfpath@retransform\expandafter{\pgfpath@retransform}%
    \global\let\pgfpath@retransform@path=\pgfutil@empty%
    \begingroup%
        \pgfprocessround{#2}{#2}%
        % Locally redefine the soft-path tokens so that they do most of the work.
        \def\pgfsyssoftpath@movetotoken{\pgfpath@retransform@point{moveto}}%
        \def\pgfsyssoftpath@linetotoken{\pgfpath@retransform@point{lineto}}%
        \def\pgfsyssoftpath@curvetosupportatoken{\pgfpath@retransform@point{curvetosupporta}}%
        \def\pgfsyssoftpath@curvetosupportbtoken{\pgfpath@retransform@point{curvetosupportblineto}}%
        \def\pgfsyssoftpath@curvetotoken{\pgfpath@retransform@point{curveto}}%
        \def\pgfsyssoftpath@rectcornertoken{\pgfpath@retransform@point{rectcorner}}%
        \def\pgfsyssoftpath@rectsizetoken{\pgfpath@retransform@point{rectsize}}%
        \def\pgfsyssoftpath@closepathtoken{\pgfpath@retransform@point{closepath}}%
        #2%
    \endgroup%
    % Now \pgfpath@retransform@path holds the transformed path
    \let#3=\pgfpath@retransform@path%
}

\def\pgfpath@retransform@point#1#2#3{%
    \pgf@process{%
        \pgf@x=#2\relax%
        \pgf@y=#3\relax%
        \pgfsettransform\pgfpath@retransform%
        \pgf@pos@transform{\pgf@x}{\pgf@y}%
    }%
    \edef\pgf@marshal{\expandafter\noexpand\csname pgfsyssoftpath@#1token\endcsname{\the\pgf@x}{\the\pgf@y}}%
    \expandafter\pgfutil@g@addto@macro\expandafter{\expandafter\pgfpath@retransform@path\expandafter}\expandafter{\pgf@marshal}%
}


\begin{document}


\begin{tikzpicture}
\begin{axis}
[
    name=firstaxis, 
    domain=-5:5, 
    xmin=-6, xmax=6, 
    ymin=-10, ymax=160, 
    enlargelimits=false
]
    \addplot[name path global=firstfunction]{e^x};

\end{axis}    

\begin{axis}
[
    name=secondaxis, 
    domain=-5:5,
    xmin=-6, xmax=6, 
    ymin=-150, ymax=150, 
    enlargelimits=false, 
    at={($(firstaxis.south)-(0,1cm)$)}, 
    anchor=north
]
    \addplot[name path global=secondfunction]{x^3};

\end{axis}

% Transform the named path.
\tikzset{transform named path=secondfunction by {shift=(secondaxis.south west)}}


\foreach \col/\from/\to in {red/west/east, green/north/south, blue/north west/south east}{
    % Draw intersection line 
    \draw 
        [color=\col, opacity=1, name path global=intersectionline] 
        (firstaxis.\from) -- (secondaxis.\to);


    % Draw intersections points
    \fill 
        [\col, name intersections={of=intersectionline and firstfunction}] 
        (intersection-1) circle [radius=2pt];    

    \fill 
        [\col, name intersections={of=intersectionline and secondfunction}] 
        (intersection-1) circle [radius=2pt];
}
\end{tikzpicture}
\end{document}

The result is the same as before.

  • But still quite nice. I had some essentially similar acrobatics on this one tex.stackexchange.com/a/96565/3235 Indeed it would be great if they somehow come to a mutual understanding of inside and outside axis behavior. – percusse Sep 27 '13 at 10:42
  • 1
    This is exactly was looking for, didnt think that I would be that complicated. Should this behaviour be regarded as a bug and there be filed to the pgfplots/tikz maintainer? – maetra Sep 27 '13 at 17:07
  • 1
    @maetra As I see it, the shifting of the low level plotted path is more of a feature request for PGFPlots. The name path global key not working with PGFPlots is also a feature request but for PGF/TikZ (I guess), as it works fine with TikZ (AFAIK). – Mark Wibrow Sep 28 '13 at 13:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.