14

I am drawing some graphs with tangents. I calculated the tangent should be centered at x=57/27 which is approximately x=2.11. I'm currently using the pos and sloped keys for nodes to draw tangents, but I'm not happy about the result; the tangents don't really align.

I think this is because the pos key calculates the actual length of the graph while the graphs don't have the same length.

I'm sure I can somehow let LaTeX do the calculations for me so as to align the slopes vertically at an x value of my choice. But how?

PS: The below document requires a working installation of gnuplot to compile.

enter image description here

\documentclass{standalone}  
\usepackage{pgfplots}
\pgfplotsset{compat=1.10}
\usetikzlibrary{arrows, decorations.markings,intersections}

\makeatletter
\tikzset{
    nomorepostaction/.code=\makeatletter\let\tikz@postactions\pgfutil@empty, 
    my axis/.style={
        postaction={
            decoration={
                markings,
                mark=at position 1 with {
                    \arrow[ultra thick]{latex}
                }
            },
            decorate,
            nomorepostaction
        },
        thin,
        -,
        every path/.append style=my axis
    }
}
\makeatother

\definecolor{headtitle}{RGB}{167,63,63}

\begin{document}
\begin{tikzpicture}
  \begin{axis}[%
      axis line style={my axis},
      xlabel = $U_A$,
      ylabel = $S$,
      xmin = 0, xmax = 3.25,
      ymin = 0,
      xtick={1,1.5},
      xticklabels={$U_{B,\mathrm{initial}}$,$U_{A,\mathrm{initial}}$},
      xticklabel style={
            inner sep=0pt,
            anchor=north east,
            rotate=35
        },
      ytick=\empty,
      height=.66\linewidth, width=\linewidth-4.5\tabcolsep,
      axis lines*=left,
      every axis y label/.style={at=(current axis.above origin),anchor=east},
      every axis x label/.style={at=(current axis.right of origin),anchor=west}]%
    \addplot+[mark={},draw=headtitle] gnuplot[raw gnuplot] {%
      set samples 51;
      plot [0:3] log(3*(3-x)+1);
      } node[sloped,above,pos=.63333,
      minimum height=1cm,minimum width=1cm] (SB) {} node[pos=0.6,pin=45:{\color{black}{$S_B$}}] {};
     \addplot+[mark={},draw=headtitle] gnuplot[raw gnuplot] {%
      set samples 51;
      plot [0:3] log(9*x^2+6*x+1);
      } node[sloped,above,pos=.83333,
      minimum height=1cm,minimum width=1cm] (SA) {} node[pos=0.25,pin=-15:{\color{black}{$S_A$}}] {};
      \addplot+[mark={},draw=headtitle] gnuplot[raw gnuplot] {%
      set samples 51;
      plot [0:3] log(9*x^2+6*x+1)+log(3*(3-x)+1);
      } node[sloped,above,pos=.775,
      minimum height=1cm,minimum width=1cm] (St) {} node[pos=0.525,pin=165:{\color{black}{$S_\mathrm{total}$}}] {};
     \path (SB.south east) edge[headtitle,very thick] node {} (SB.south west);
     \path (SA.south east) edge[headtitle,very thick] node {} (SA.south west);
     \path (St.south east) edge[headtitle,very thick] node {} (St.south west);
  \end{axis}%
\end{tikzpicture}%
\end{document}
2
  • You mean the lines should be centered at the same x value?
    – percusse
    Aug 24, 2014 at 16:24
  • @percusse Yeah, exactly that.
    – 1010011010
    Aug 24, 2014 at 16:25

4 Answers 4

18
+50

You can use the approach from pgfplots: Placing node on a specific x-position (which used the same basic idea as Thruston's answer: finding the intersection with a vertical line).

I've removed stuff from your example that wasn't related to the problem at hand, and used the PGF math engine instead of gnuplot, which works fine in this case.

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

\makeatletter
\def\parsenode[#1]#2\pgf@nil{%
    \tikzset{label node/.style={#1}}
    \def\nodetext{#2}
}

\tikzset{
    add node at x/.style 2 args={
        name path global=plot line,
        /pgfplots/execute at end plot visualization/.append={
                \begingroup
                \@ifnextchar[{\parsenode}{\parsenode[]}#2\pgf@nil
            \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)
            node [label node]{\nodetext};
            \endgroup
        }
    }
}
\makeatother

\begin{document}
\begin{tikzpicture}
  \begin{axis}[%
      xmin = 0, xmax = 3.25,
      ymin = 0,
      domain=0:3,
      samples=50,
      tangent/.style={
            add node at x={2}{
                [
                    sloped, 
                    append after command={(\tikzlastnode.west) edge [thick, red!75!black] (\tikzlastnode.east)},
                    minimum width=2cm
                ]
            }      
      }
      ]%
    \addplot [gray, tangent] {ln(3*(3-x)+1)};
    \addplot [gray, tangent] {ln(9*x^2+6*x+1)};
    \addplot [gray, tangent] {ln(9*x^2+6*x+1)+ln(3*(3-x)+1)};
  \end{axis}%
\end{tikzpicture}%
\end{document}
1
  • 2
    Should I check the manual for this? :p
    – percusse
    Aug 26, 2014 at 18:42
9

Just for fun with PSTricks. The functions are intentionally made different from your case to let you modify them by yourself as an exercise.

As the question is not easy to understand, I provide two answers. One of them should meet your requirement.

Option 1: Equidistant tangent segments

In this option, I make the tangent segments have the same length.

\documentclass[pstricks,border=12pt,12pt]{standalone}

\usepackage{pstricks-add}

\begin{document}
\begin{pspicture}[algebraic](-1,-3)(7,3)
    \psaxes(0,0)(-1,-3)(7,3)
    \foreach \f/\c in {{3-2*Euler^(-x/2)}/red,2*sin(x)/green,2*cos(x)/blue}
        {
            \psplot[linecolor=\c]{-1}{7}{\f}
            \psplotTangent[arrows=<->]{5}{1}{\f}
        }
\end{pspicture}
\end{document}

enter image description here

Option 2: Justified tangent segments

In this option, the left (and right) end point of each tangent segment is aligned with the same vertical line.

\documentclass[pstricks,border=12pt,12pt]{standalone}

\usepackage{pstricks-add}

\def\list{{3-2*Euler^(-x/2)}/red,2*sin(x)/green,2*cos(x)/blue}

\begin{document}
\begin{pspicture}[algebraic](-1,-3)(7,3)
    \psaxes(0,0)(-1,-3)(7,3)
    \foreach \f/\c in \list {\psplot[linecolor=\c]{-1}{7}{\f}}%
    \psclip{\psframe[linestyle=none,linewidth=0](2,-3)(4,3)}
        \foreach \f/\c in \list {\psplotTangent{3}{5}{\f}}%
    \endpsclip
\end{pspicture}
\end{document}

enter image description here

1
  • Be carefull, foreach leaves unnecessary white spaces that will interfere the drawing. So % must be appended at the end of \foreach. Aug 26, 2014 at 18:51
8

Here's a way to plot what you want in Metapost. MP provides an intersectiontimes operation which finds the points where two paths cross - here I've used it to find the points where each curve intersects an invisible vertical line at x=57/27. I then used the direction t of p and point t of p constructs to find the tangent and draw it in the right place on each function curve.

enter image description here

prologues:=3;
outputtemplate:="%j%c.eps";
beginfig(1);

% the two functions we require - see the Metafont book about mlog
vardef f(expr x) = mlog(3*(3-x)+1)/256 enddef;
vardef g(expr x) = mlog(9*x**2+6*x+1)/256 enddef;

% tranformation used to scale everything up neatly 
transform t; t = identity xscaled 3cm yscaled 7mm;

xmin = 0;
xmax = 3;
tangent_point = 57/27;

% define the three paths to plot, plus one that intersects them all
path a, b, total, xx;
b     = ((0,f(0))      for x=0.1 step 0.05 until xmax+eps: -- (x,f(x))      endfor) transformed t;
a     = ((0,g(0))      for x=0.1 step 0.05 until xmax+eps: -- (x,g(x))      endfor) transformed t;
total = ((0,f(0)+g(0)) for x=0.1 step 0.05 until xmax+eps: -- (x,f(x)+g(x)) endfor) transformed t;
xx    = ((tangent_point,0) -- (tangent_point,10)) transformed t; % a vertical line shifted along a bit

% draw the functions
drawoptions(withcolor .67 red);
draw a; draw b; draw total;

% draw the tangents at the points of intersection
drawoptions(withpen pencircle scaled 1 withcolor .45 red);
forsuffixes $=a,b,total:
  (t$,tt$) = $ intersectiontimes xx; 
  draw ((left--right) scaled 1cm rotated angle direction t$ of $ shifted point t$ of $);
endfor
drawoptions();

% axes
drawarrow origin -- (1.1xmax,0) transformed t; label.rt (btex $U_A$ etex, (1.1xmax,0) transformed t);
drawarrow origin -- (0,8)       transformed t; label.lft(btex $S$   etex, (0,8)       transformed t);

% labels
label.ulft(btex $S_{\rm total}$ etex, point 20 of total);
label.ulft(btex $S_A$           etex, point 20 of a);
label.ulft(btex $S_B$           etex, point 20 of b);

endfig;
end
0
2

Using the tzplot package:

enter image description here

\documentclass[tikz]{standalone}
    
\usepackage{tzplot}

\begin{document}

\begin{tikzpicture}[xscale=2,yscale=.7,very thin,font=\scriptsize]
% axes and ticks
\tzaxes(3.5,6){$U_A$}{$S$}
\tzticksx*[gray](0:2pt){1,1.5}
\begin{scope}[inner sep=0pt]
\tznode(1,0){$U_{B,initial}$}[anchor=north east,rotate=35]
\tznode(1.5,0){$U_{A,initial}$}[anchor=north east,rotate=35]
\end{scope}
% functions
\def\SA{ln(9*(\x)^2+6*\x+1)}
\def\SB{ln(3*(3-\x)+1)}
\def\ST{\SA+\SB}
\tzfn\SA[0:3]
\tzfn\SB[0:3]
\tzfn\ST[0:3]
% labels
\tzvXpointat{SA}{.3}(A)
\tzvXpointat{SB}{2}(B)
\tzvXpointat{ST}{1}(T)
\tznode(A){}[pin=-15:{$S_A$}]
\tznode(B){}[pin= 15:{$S_B$}]
\tznode(T){}[pin=165:{$S_{total}$}]
% slopes
\tzslopeat[red]{SA}{57/27}{1cm}
\tzslopeat[red]{SB}{57/27}{1cm}
\tzslopeat[red]{ST}{57/27}{8mm}
\end{tikzpicture}

\end{document}

Answer to How to draw parabolic graph and its tangent

enter image description here

\documentclass[tikz]{standalone}
    
\usepackage{tzplot}

\begin{document}

\begin{tikzpicture}[scale=5]
\tzaxes[-](-.7,-.5)(.7,.5){Y}[a]{X}[at start,r]
\tzshoworigin{O}[ar]
\def\Fx{-(\x)^2}
\tikzset{>=to}
\tzfn[-->--=.25]\Fx[-.5:.5]
\tzvXpointat*{Fx}{.3}(P){P}[45]
\tztangent[red]"tan"{Fx}(P)[.1:.5]
\tzhfn(P)[.1:.5]
\tzline(P)(P|-0,-.4)
\tzvXpointat{tan}{.4}(T)
\tzanglemark(P|-0,-.4)(P)(T){$\theta$}[pos=.8](5pt)
\end{tikzpicture}

\end{document}

Answer to How to draw a tangent line to an arbitrary `\addplot`

enter image description here

\documentclass[tikz]{standalone}
    
\usepackage{tzplot}

\begin{document}

\begin{tikzpicture}
\tzhelplines(5,6)
\tzaxes[-](-.5,-.5)(5,5){$x$}{$y$}
\tzparabola[red,thick]"AA"(1,3)(3,1)(5,3){$f(x)$}[r]
\tztangentat[blue,thick]"BB"{AA}{2}[0:3]{$f'(x)$}[r]
\tzvXpointat*[red]{AA}{2}(M)
\tzvXpointat*{BB}{0}(N)
\tzhfn[densely dashed](M)[-.5:2.5]
\tzhfn[densely dashed](N)[-.5:0]
\tzline[<->]<-.2,0>(N){$p$}[l](M-|0,0)
\end{tikzpicture}

\end{document}
3
  • I used the newest version 2 of tzplot to replicate the code above, but got the error Undefined control sequence. l.7694 \AddToHook {env/tikzpicture/before}...
    – cheyp
    Apr 7, 2022 at 20:30
  • 1
    @ cheyp Using tzplot is just using tikz with some tikz-libraries preloaded. I think tzplot macros do not produce that error. (If you use texlive older than 2020, update may be one of the solutions to your problem, I guess.)
    – I. Cho
    Apr 8, 2022 at 9:19
  • You are right. The most recent update of texlive solved it. Thanks.
    – cheyp
    Apr 9, 2022 at 9:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .