Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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}
share|improve this question
    
    
@PeterGrill but wouldn't I have to specify the point in the curve the tangent line has to go through? (let me know if I misunderstood it). I tried that, but it is not really what I want. I want to specify a point outside of the curve (I_0), and I want a line tangent to the curve, and I don't care where in the curve the tangent point is (what I care about is the point outside of the curve, I_0) –  Vivi Mar 15 '12 at 2:29
    
Ok, that makes sense. I guess it comes down to figuring out the math to compute the tangent... –  Peter Grill Mar 15 '12 at 2:34
    
You can try using the tkz-fct package. It can compute the tangent of a curve. It uses Gnuplot. –  Frédéric Mar 15 '12 at 2:35
    
One way of doing this (but extremely inefficient) would be to take samples points on the curve, and iterate over the tangents to the curve at that point, and find all the points on the curve with the minimum (within some threshold) distance from the tangent line to the given point (there may be more than one), and then draw those lines. But, there must be an easier way. –  Peter Grill Mar 15 '12 at 2:47

3 Answers 3

up vote 6 down vote accepted

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

share|improve this answer
1  
Wow! That was super-clever! I will need to adjust this to have the line continue on after the tangent, and also to be able to define the starting point (the i0) in the \draw command, since I might have more than one initial point in a figure, but it seems like this will work! (I am also quite busy now, so I can't play with the answer at the moment and won't accept it as correct until I have the chance to look at this more closely) –  Vivi Mar 17 '12 at 4:24
    
Interesting approach. I was afraid by time and precision but perhaps I was wrong. –  Alain Matthes Mar 17 '12 at 6:17
2  
Thanks for pgfdecoratedangle. I discover this useful macro. In a first time, I did not like the decoration library, but now with your code I think it's time to study this part of TikZ ! –  Alain Matthes Mar 17 '12 at 6:33
1  
great, that's a good idea –  rpapa Mar 17 '12 at 7:52
    
@Vivi Thanks, hope it helps. I think it's better than nothing however it is pretty fragile. So handle with care :) –  percusse Mar 17 '12 at 23:50

Update

It's perhaps possible with Asymptote but not with TikZ. You need to resolve an equation to get the first tangent from I_0 to u_1 , because you don't know where is the point of intersection with the curve.

I think a fine method without (and perhaps with) equations of the curves is to use a visual way:

Step 1 We try to get an approximate value for the slope of the tangente from I_0 to u_1. This tangent has an equation like y=px+4.65. p is the slope. First we draw some lines with different values of p. A good interval is -1.2,-1.1,...,-0.8.

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,intersections}
\begin{document}
\begin{tikzpicture}
    % u_1
    \node (h) at (1.5,4.2) {};
    \node (b) at (2.4,2.3) {};
    \node (j) at (4.4,1.6) {};
    \draw[very thin,name path=curve 1] plot[smooth,tension=0.9] 
          coordinates {(h) (b) (j)} node[right]{$u_{1}$};
    \coordinate[label=left:{\scriptsize$I_{0}$}] (i0) at (0,4.65);
 \foreach \p in {-1.2,-1.1,...,-0.8}
 {\draw[blue,very thin] (0,4.65) -- (5,5*\p+4.65);}    
\end{tikzpicture}
\end{document}

enter image description here

Now we know that the slope is something like -1.0,...,-0.96

Step 2 With intersections we can find the better value :

I named curve 2 and curve 3 the lines defined by y=-0.9828*x+4.65' andy=-0.96*x+4.65' and I try to find the intersections of the curves with the curve 1. I found -0.9828with trials. With for example -0.99 I get an error because the curves have no intersection.

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

\begin{document}

\begin{tikzpicture}
    % u_1
    \node (h) at (1.5,4.2) {};
    \node (b) at (2.4,2.3) {};
    \node (j) at (4.4,1.6) {};
    \draw[very thin,name path=curve 1] plot[smooth,tension=0.9] coordinates {(h) (b) (j)} node[right]{$u_{1}$};
    \coordinate[label=left:{\scriptsize$I_{0}$}] (i0) at (0,4.65);

 \draw[green,very thin,name path=curve 3] (0,4.65) -- (5,-5*0.96+4.65);
 \fill [name intersections={of=curve 1 and curve 3, name=i, total=\t}]
         [orange, opacity=0.5, every node/.style={above left, black, opacity=1}]
        \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node {}};  

 \draw[blue,very thin,name path=curve 2] (0,4.65) -- (5,-5*0.9828+4.65);
 \fill [name intersections={of=curve 1 and curve 2, name=i, total=\t}]
         [red, opacity=0.5, every node/.style={above left, black, opacity=1}]
        \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node {}};            
\end{tikzpicture}
\end{document} 

enter image description here

p=-0.9828 seems to be a fine value.

Step 3

Now we know that the slope of the tangent to u_0 is -0.9828. The tangent has for equation y=-0.9828 *x +m

You can draw several lines with different values of m. A good interval seems to be m in {5,5.5,...,7}

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,intersections}
\begin{document}
\begin{tikzpicture}
    % u_0
    \node (e) at (2.2,4.9) {};
    \node (f) at (3,3) {};
    \node (g) at (5,2.3) {};
    \draw[thick,name path=curve 4] plot[smooth,tension=0.9] coordinates {(e) (f) (g)} node[right]{$u_{0}$};     
\foreach \m in {5,5.5,...,7}
 {\draw[red,very thin] (0,\m) -- (5,-0.9828*5+\m);} 
\end{tikzpicture}
\end{document}

enter image description here

Step 4 Now we know that m is very near 6.0. I added \def\m{5.9}to modify this value easily . I name the curve u_0 the curve 4 and the line is named curve 5. I look at the intersections.

I use here only two values m=6.2 and `m=5.9410``

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

\begin{document}

\begin{tikzpicture}
    % u_0
    \node (e) at (2.2,4.9) {};
    \node (f) at (3,3) {};
    \node (g) at (5,2.3) {};
    \draw[thick,name path=curve 4] plot[smooth,tension=0.9] coordinates {(e) (f) (g)} node[right]{$u_{0}$};     

   \def\m{6.2}
  \draw[green,very thin,name path=curve 5] (0,\m) -- (5,-0.9828*5+\m);
 \fill [name intersections={of=curve 4 and curve 5, name=i, total=\t}]
         [orange, opacity=0.5, every node/.style={above left, black, opacity=1}]
        \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node {}};   

  \def\m{5.9410}
  \draw[red,very thin,name path=curve 5] (0,\m) -- (5,-0.9828*5+\m);
 \fill [name intersections={of=curve 4 and curve 5, name=i, total=\t}]
         [red, opacity=0.5, every node/.style={above left, black, opacity=1}]
        \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node {}};    

\end{tikzpicture}
\end{document}   

enter image description here

Final Step

Finally I take p=-0.992 and m=5.941 and the result is :

(I used a clip to limit the lines like you want)

\documentclass{standalone}
\usepackage{tikz}
\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);
    \clip (0,0) rectangle (8,7) ;
    \draw[blue,very thin] (0,4.65) -- (7,-7*0.9828+4.65); 
    % second budget line (I_0 to p_1)
    \draw[red,very thin] (0,5.9410) -- (7,-0.9828*7+5.9410);   
\end{tikzpicture}
\end{document}  

enter image description here

share|improve this answer
    
So how do I know which of the slopes is the "right" one? Should I count the lines or something like that? What I am saying is: tikz itself doesn't know it reached the curve, right, I must after drawing the picture figure out which of the lines is better? I think this is better than what I have, but I was hoping for something even better. To be honest, I thought one of your packages would have an easy solution for this... –  Vivi Mar 15 '12 at 9:07
    
No sorry my packages can do only simple stuff ! I think that it's not possible with tikz and tex to do what you want. The better way is to use mathematica, maple or the free xcas. The difficult point is to calculate the distance between the line and the curve. And you don't know the polynomial expression of u_1! Now to choice the good slope, you use the "dichotomy" method. First you try with {-1.2,-1.18,...,-0.9} and then you reduce this set : {-0.995,-0.990} etc. –  Alain Matthes Mar 15 '12 at 10:02
    
I updated the answer, now I use intersections library but you need to use trials and errors. The slope is different because here I take care of the line width. The last value before to get an error is -0.9828 –  Alain Matthes Mar 15 '12 at 10:56

(Major Update)

Here is a solution that draws the required diagram. It should be used for a demonstration purpose (show the ideas in class for example). The result is

enter image description here

My code is a little heavy and inelegant. Some macros would make it better, but I was focusing on getting the task done. The code is:

\documentclass[border=5pt]{standalone}

\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}
\begin{tikzpicture}[point/.style={fill=blue,circle,minimum size=2pt,inner sep=0pt}]

\pgfmathsetmacro{\k}{0.2}

\draw[->] (0,0) -- (5,0) node[right] {$x_1$};
\draw[->] (0,0) -- (0,5) node[left] {$x_2$};

\coordinate[point] (a) at (1,2);
\coordinate[point] (c) at (1.25,1.3);
\coordinate[point] (b) at (2,1);
\coordinate[point] (i) at (0,2.5);

\coordinate (v) at ($(c) - (i)$);

%This is a (quadratic) Bézier curve. You can use Tikz' Bézier curves and adjust the control points  to do the same.
\draw[thick,blue] let
            \p{a} = (a),
            \p{c} = (c),
            \p{b} = (b),
            \p{v1} = ($(c) - \k*(v)$),
            \p{v2} = ($(c) + \k*(v)$)
            in
            plot[domain=0:1,variable=\t,samples =20,smooth] ({(1-\t)^2*\x{a} + 2*(1-\t)*\t*\x{v1} + (\t)^2*\x{c}}, {(1-\t)^2*\y{a} + 2*(1-\t)*\t*\y{v1} + (\t)^2*\y{c}})
            plot[domain=0:1,variable=\t,samples =20,smooth] ({(1-\t)^2*\x{c} + 2*(1-\t)*\t*\x{v2} + (\t)^2*\x{b}}, {(1-\t)^2*\y{c} + 2*(1-\t)*\t*\y{v2} + (\t)^2*\y{b}});

\draw[red] let
            \p{i} = (i),
            \p{v} = (v)
            in
            (i) -- ++(${-\y{i}/\y{v}}*(v)$) coordinate[point];

\coordinate[point] (d) at (1.2,2.3);
\coordinate[point] (f) at (1.55,1.6);
\coordinate[point] (e) at (2.3,1.3);

\draw[thick,blue,dashed] let
            \p{d} = (d),
            \p{f} = (f),
            \p{e} = (e),
            \p{v1} = ($(f) - \k*(v)$),
            \p{v2} = ($(f) + \k*(v)$)
            in
            plot[domain=0:1,variable=\t,samples =20,smooth] ({(1-\t)^2*\x{d} + 2*(1-\t)*\t*\x{v1} + (\t)^2*\x{f}}, {(1-\t)^2*\y{d} + 2*(1-\t)*\t*\y{v1} + (\t)^2*\y{f}})
            plot[domain=0:1,variable=\t,samples =20,smooth] ({(1-\t)^2*\x{f} + 2*(1-\t)*\t*\x{v2} + (\t)^2*\x{e}}, {(1-\t)^2*\y{f} + 2*(1-\t)*\t*\y{v2} + (\t)^2*\y{e}});

\draw[dashed,red] let
            \p{f} = (f),
            \p{v} = (v)
            in
            ($(f) + {-\x{f}/\x{v}}*(v)$) coordinate[point]  -- ($(f) + {-\y{f}/\y{v}}*(v)$) coordinate[point];

\draw[green] let
            \p{i} = (i),
            \p{f} = (f),
            \p{w} = ($(f) - (i)$)
            in
            (i) -- ++(${-\y{i}/\y{w}}*(\p{w})$) coordinate[point];


\end{tikzpicture}
\end{document}

(Initial Answer)

As I mentioned in my comment, the package tkz-fct computes tangents, you can check the documentation, there are good examples. It uses Gnuplot. As I don't have Gnuplot I could not build an example for you.

What follows is another approach, using the intersections library.

\documentclass[border=5pt]{standalone}

\usepackage{tikz,pgfplots}
\usetikzlibrary{calc}
\usetikzlibrary{intersections}

\begin{document}

\begin{tikzpicture}

\coordinate (a) at (0,0);
\coordinate (b) at (2,2);
\coordinate (c) at (4,1);

\draw[name path=curve,smooth] plot coordinates {(a) (b) (c)};
\path[name path= circle] (b) circle[radius=1pt];

\draw[red, name intersections={of=curve and circle},shorten <=-2cm,shorten >=-2cm]
    (intersection-1) -- (intersection-2);

\end{tikzpicture}

\end{document}

The result is

enter image description here

Setting a smaller circle radius will give a more precise result.

As you can see, my solution uses shorten. A possible way around this is to use intersections a second time to find where the points must be along the axis lines.

share|improve this answer
    
Maybe I am missing something, but how would you know that the point of intersection of the tangent is (b)? I believe in the question you are only given a curve and an external point. You are not given the point on the curve on which the tangent goes through the given point. I believe that this solution requires knowledge of the point on the curve. –  Peter Grill Mar 15 '12 at 4:35
    
It was my understanding that the tangents in the question were drawn by trial and error. In addition, the curves were drawn as plots from known points. My approach is to choose one of the points and draw the tangent there. –  Frédéric Mar 15 '12 at 4:47
    
Thank you Peter. Rereading the question, I see that my answer does not completely answer the question, as the tangent is to be parallel to another (given) line. –  Frédéric Mar 15 '12 at 4:55
    
@Frédéric, my problem is exactly that I don't want to specify the tangent point. I did that for some of the other figures, but then when I need to change the slope of the straight line (the budget line), that tangent point is not going to work again, and I need to once more look for a "tangent" point that works... –  Vivi Mar 15 '12 at 9:02
2  
Very good update. Interesting approach ! –  Alain Matthes Mar 17 '12 at 6:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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