[Tex/LaTex] Line tangent to a a curve (using plot or controls), starting at a point

tikz-pgf

I need to draw lines that are parallel to a curve starting at a point.

The curve could be a curve drawn using 3 point as my example below did, but alternatively could be a curve drawn using the \draw () .. controls () and () .. () command. Preferably it would work either way.

This is the picture of what I am after:

enter image description here

Explanation

I have to curves, u_0 and u_1, and a point I_0. I want to find the line that goes from this point (I_0) and is tangent to the first curve (u_0), and the line that goes from (I_0) and is tangent to the second curve (u_1).

Then, I want to find a line that is parallel to the line tangent to (u_1), and this line needs to be tangent to (u_0). If the line can start and stop at the axis without using the shorten commands even better.

For now, I found the tangents and the start and finish points of the parallel line with trial and error, but I have dozens of such pictures to drawn and I need a better way of doing this.

Here is my code:

\documentclass{standalone}
\usepackage{tikz,pgfplots}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}
    % axes
    \coordinate (origin) at (0,0);
    \draw (origin) -- (0,6.5) node[left]{$x_{2}$};
    \draw (origin) -- (9.5,0) node[below]{$x_{1}$};
    % u_0
    \node (e) at (2.2,4.9) {};
    \node (f) at (3,3) {};
    \node (g) at (5,2.3) {};
    \draw[thick] plot[smooth,tension=0.9] coordinates {(e) (f) (g)} node[right]{$u_{0}$};
    % u_1
    \node (h) at (1.5,4.2) {};
    \node (b) at (2.4,2.3) {};
    \node (j) at (4.4,1.6) {};
    \draw[thick] plot[smooth,tension=0.9] coordinates {(h) (b) (j)} node[right]{$u_{1}$};
    % first budget line (I_0 to p_0)
    \coordinate[label=left:{\scriptsize$I_{0}$}] (i0) at (0,4.65);
    \draw (i0) -- (8.1,0); 
    % second budget line (I_0 to p_1)
    \coordinate (d) at (4.67,0) {};
    \draw (i0) -- (d);
    % dashed line
    \draw[densely dashed,style={shorten >=2.8cm,shorten <=-4.64cm}] (2.68,3.3)   -- +($(i0)-(d)$);
\end{tikzpicture}
\end{document}

Best Answer

I think a decoration idea can be utilized here. As can be seen in the manual, the decoration declaration has a particular useful register that keeps the decoration angle defined as the tangent at the particular decoration segment. So I played around with that idea and here is the result:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations}
\newcounter{loopcoun}
\setcounter{loopcoun}{0}
\makeatletter
\pgfdeclaredecoration{findtan}{initial}{
\state{initial}[width=1mm,next state=findit]{
}
\state{findit}[width=1mm]{
\ifnum\value{loopcoun}<1
        \pgfpointanchor{i0}{center}
        \pgf@xa = \pgf@x
        \pgf@ya = \pgf@y
        \pgfmathparse{ifthenelse(atan2(\pgf@xa,\pgf@ya)<\pgfdecoratedangle,1,}
        \ifx\pgfmathresult\@empty\relax
        \pgfmoveto{\pgfpointorigin}
        \pgflineto{\pgfpointanchor{i0}{center}}
        \setcounter{loopcoun}{1}
        \else
        \fi
\else\fi
\pgfusepath{stroke}
    }
\state{final}[1mm]{
\setcounter{loopcoun}{0}
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\draw[style=help lines] (0,0) grid[step=1cm] (8cm,5cm);
\node[fill,inner sep=1pt,circle] (i0) at (0,3) {};

\path[draw,decoration=findtan,postaction={decorate}] (2,3) .. controls (0,1) and (4,0) .. (5,1);
\path[red,draw,decoration=findtan,postaction={decorate}] (3,4) .. controls (2,1) and (5,2) .. (6,1);
\end{tikzpicture}
\end{document}

enter image description here

The basic idea is that TikZ starts travelling on the curve little by little (given by the width option of the \state declaration) and do some stuff defined by the commands of that particular state. Basically, that defines the approximation resolution (reduce if necessary!). Here I am calculating the angle of the tangent line and comparing it with the given (i0) point. If it satisfies the constraint I am drawing a line to the point and incrementing the counter such that the code gets executed only once. So this is a proof of the concept. If you don't increment the counter it would give all the points satisfying the condition. Note that we are really exploiting something that is not meant to behave like this at all so expect all kinds of strange results. At least it doesn't draw anything if there is no such point, please play around with the location of (i0) to see if there is any bugs.

I wrote up a slightly messy code that seemingly does what you wanted. The idea is that you supply some initial points and then draw the curves indicating the particular tangent point. Then if any, they will be collected as nodes under the generic name (c-#). With this I did some tedious calculations using some geometry and draw the lines. There is much to improve obviously and hope it helps.

edit: the curves should start from top left to bottom right since the code marks the first eligible point. first of possibly many shortcomings...

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations,calc}
\newcounter{loopcoun}
\newcounter{cocounter}
\setcounter{cocounter}{0}
\setcounter{loopcoun}{0}
\makeatletter

\pgfkeys{/tikz/find tangent/.style={decoration=findtan,depoint=#1,postaction=decorate}}
\pgfkeys{/tikz/depoint/.code= \edef\pgf@pointname{#1}}

\pgfdeclaredecoration{findtan}{initial}{
\state{initial}[width=1mm,next state=findit]{
}
\state{findit}[width=1mm]{
\ifnum\value{loopcoun}<1
        \pgfpointanchor{\pgf@pointname}{center}
        \pgf@xa = \pgf@x
        \pgf@ya = \pgf@y
        \pgfmathparse{ifthenelse(atan2(\pgf@xa,\pgf@ya)>\pgfdecoratedangle,1,0)}
        \ifnum\pgfmathresult>0
        \stepcounter{cocounter}{1}
        \pgfcoordinate{c-\the\value{cocounter}}{\pgfpointorigin}
        \setcounter{loopcoun}{1}
        \else
        \fi
\else\fi
\pgfusepath{stroke}
    }
\state{final}[1mm]{
\setcounter{loopcoun}{0}
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\draw[style=help lines] (0,0) grid[step=1cm] (9cm,5cm);
\node[fill,inner sep=1pt,circle] (p0) at (0,3) {};
\node[fill,inner sep=1pt,circle] (p1) at (0,4) {};

\draw[find tangent=p0] (2,3) .. controls (0,1) and (4,0) .. (5,1);
\path[find tangent=p0] (3,4) .. controls (2,1) and (5,2) .. (6,1);
\draw[red,find tangent=p1] (3,4) .. controls (2,1) and (5,2) .. (6,1);

\draw let \p1 = (p0), \p2 = (c-1),\n2 = {atan2(\x1-\x2,\y1-\y2)},\n3 = {-\y1/sin(\n2)} in (p0) -- ++(\n2:\n3); 
\draw let \p1 = (p0), \p2 = (c-2),\n2 = {atan2(\x1-\x2,\y1-\y2)},\n3 = {-\y1/sin(\n2)} in (p0) -- ++(\n2:\n3); 
\draw[dashed] let \p1 = (p1), \p2 = (c-3),\n2 = {atan2(\x1-\x2,\y1-\y2)},\n3 = {-\y1/sin(\n2)} in (p1) -- ++(\n2:\n3); 
\end{tikzpicture}
\end{document}

enter image description here