# Draw a tangent to curve at specified angle using tikz

I would like to draw a tangent line to a curve, and I know there are elegant solutions to do so (eg. How to draw tangent line of an arbitrary point on a path in TikZ). However, these approaches usually draw the tangent at a point that is specified by its relative position on the curve. Instead, what I would like to do is give a specified angle and have tikz find the position itself. (I know there could be multiple, but for my purposes I don't have that issue.)

Here is a MWE:

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

\begin{document}
\begin{tikzpicture}
\draw (0,6) -- (0,0) -- (9,0);
\draw[blue] (0,5) to[out=-5,in=100] (8,0);
\begin{scope}[shift={(5.4,3.6)}]
\draw[red,rotate=-30] (-2,0) -- (2,0);
\draw (0,0) circle (3pt);
\end{scope}
\end{tikzpicture}
\end{document}


Essentially, what I'm after is how to automate the search for the "shift" parameter, which I here just eyeballed with trial & error.

• While PGF can calculate the angle for a relative poisition (time) of a curve it doesn't provide a function to calculate the inverse. You will need some mathematical formula (first derivative) for that, I believe. You could also try a bunch of points, say 0, .05, …, .95, .1 and choose the one that's closest to the desired angle. It gets even more interesting when you're not looking at only one Bézier curve but a path comprised out of multiple segments. Jan 11 at 14:36
• Basically, you need to do the inverse of this answer, i.e. finding a t for a specific angle. Jan 11 at 14:43
• Because I need two parallel lines in my case, it's easier to eyeball the shift parameter rather than using tangents and estimating the pos parameter. I can keep doing it this way, I was just wondering if there is a more elegant approach. Thanks though! Jan 11 at 14:50

The bezier bounding box from the TikZ library bbox can do these types of calculations, but it is not meant for this purpose, so it takes some tricks to extract the coordinate.

\documentclass[tikz, border=1cm]{standalone}
\usetikzlibrary{bbox, intersections}
\begin{document}
\begin{tikzpicture}
\newcommand{\mya}{-30}
\draw (0,6) -- (0,0) -- (9,0);
\draw[blue] (0,5) to[out=-5, in=100] (8,0);
\begin{scope}[local bounding box=lbb, bezier bounding box]
\path[name path=curve, rotate=-\mya] (0,5) to[out=-5, in=100] (8,0);
\path[name path=max] ([yshift=-0.001pt]lbb.north west) -- ([yshift=-0.001pt]lbb.north east);
\path[name intersections={of=max and curve}] (intersection-1) coordinate(c);
\end{scope}
\begin{scope}[transform canvas={rotate=\mya}]
\draw[red] (c) +(-2,0) -- +(2,0);
\end{scope}
\end{tikzpicture}
\end{document} • Thanks!! This looks really promising – it's by far the least custom code of the solutions given so far. Would you please be able to explain what the two last lines in the scope do, so that I'm better able to adapt the code if needed? Jan 12 at 1:31
• The code rotates the curve by the given angle, finds the bounding box, and finds the intersection of the curve and this box -that is the maxima of the rotated curve. The maxima point is rotated back on the original curve and circle and line is drawn. You can change \paths to \draws to see what is happening. Jan 12 at 7:21 I propose a solution that uses a decoration and a pic element, the tangent line.

1. The decoration phSTrajectory takes one argument (the number of points) and creates equally spaced points P_i along the curve and the tips U_i of the tangent unit vectors.

2. The pic element tangent takes two arguments, the angle of the tangent we are looking for (it must exist) and the number of points, and draws the tangent line (an approximation).

In the figure, ther is your drawing of the tangent at -30 degrees in red, and the corresponding pic in black. I added the tangent with sloppe -65 in thick red.

Remark I marked the corresponding point (a filled circle) for visual verification.

The code

\documentclass[11pt, margin=.5cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{math, calc, decorations.markings}

\begin{document}
\tikzset{%
phSTrajectory/.style={% nb of steps
decoration={%
markings,
mark=between positions 0 and 1 step 1/#1 with {
\tikzmath{%
{
\path (0, 0) coordinate[name=P_\pgfkeysvalueof{%
/pgf/decoration/mark info/sequence number}];
\path (1, 0) coordinate[name=tipU_\pgfkeysvalueof{%
/pgf/decoration/mark info/sequence number}];
};
}
}
},
postaction=decorate
},
pics/tangent/.style 2 args={% angle / numbrer of points
code={
\tikzmath{%
integer \j;
real \a;
\j = -1;
coordinate \M;
for \i in {1, ..., #2}{%
\M = ($(tipU_\i) -(P_\i)$);
\a = {atan2(\My, \Mx)};
if \a<-#1 then {%
if \j==-1 then {%
\j = \i -1;
{
\draw ($(P_\j)!-1!(tipU_\j)$) -- ($(P_\j)!1!(tipU_\j)$);
\filldraw[blue] (P_\j) circle (1pt);
};
} else {};
} else {};
};
}
}
}
}
\begin{tikzpicture}[evaluate={\N = int(220);}]
\draw (0, 6) -- (0, 0) -- (9, 0);
\draw[blue, phSTrajectory={\N}] (0, 5) to[out=-5, in=100] (8, 0);
\path pic {tangent={30}{\N}};
\path pic[red, very thick] {tangent={65}{\N}};

\begin{scope}[shift={(5.4, 3.6)}]
\draw[red, rotate=-30] (-2, 0) -- (2, 0);
\draw (0, 0) circle (3pt);
\end{scope}
\end{tikzpicture}
\end{document}

• Am I understanding right that this basically computes the angle at many points to find the one closest to what I'm looking for? Jan 12 at 1:36
• Indeed. It is close to the realm of numerical analysis. In case you need a better precision, some work must be done. I noticed that one can not keep increasing the number of points of the initial division. Jan 12 at 4:28

You can force the curve to pass through the tangency point T with a known angle. Like this:

\documentclass[tikz]{standalone}

\begin{document}
\begin{tikzpicture}
\coordinate (T) at (5.4,3.6);
\draw (0,6) -- (0,0) -- (9,0);
\draw[blue] (0,5) to[out=-5,in=150] (T) to [out=-30,in=100] (8,0);
\draw[red] (T) ++ (150:3) --++ (-30:6);
\draw (T) circle (3pt);
\end{tikzpicture}
\end{document} • Thanks! I've used this trick before.. alas, in this case it isn't really workable in my code because I've already used up the degrees of freedom for the curve itself. Jan 12 at 1:28

As suggested by my comment, here's a mathematical solution that works like \pgfpointcurveattime and \pgftransformcurveattime but with …atangle.

For TikZ, I'm using pos angle = <angle> that switches out the curve timer for the curve angle timer.

The core of this answer is \pgfpointcurveatangle@ which will “return” the time t as \@time which then will be forwarded by the …curveatangles to the …curveattimes. (This technically does do work twice because the …curveattimes also return the angle for the use with sloped.)

Instead of PGFMath, I'm using \fpeval since I've run into problems with basic PGFmath.
This probably can be converted so that it uses the fpu library but I don't have much experience with it.

Using pos angle with any other path than an explicit Bézier should do nothing. “Explicit” means that it will only work with .. controls or the various out/in options.
While arcs are drawn as Bézier curves on the lowest level, they use a different timer and won't handle the angle timer.

Lastly, I've added a node to show what PGF thinks the angle of the tangent is which, expectedly, is a bit off.

To use it, just place a coordinate, node or pic along a curve and instead of pos (or its implicit versions like midway) use pos angle. If you want the node or pic also angled use sloped and possibly allow upside down.

In your case, I've added a pic called cs which just places three coordinates named <name>, <name>-x and <name>-y (where <name> is the name of the pic). The pic cs style can then be used to shift and rotate the coordinate system as if you were drawing in the pic.

Then you can draw your tangent as such:

\draw[blue] (0,5) to[out=-5,in=100]
pic[pos angle=-30, sloped] (a) {cs} (8,0);
\draw[pic cs=a, red] (-2,0) -- (2,0);


Don't forget allow upside down otherwise your cs might be rotated.
With

pics/cs/.prefix style={/tikz/allow upside down, /tikz/sloped}


you can make every cs pic automatically sloped and upside down.

## Code

\documentclass[tikz,convert]{standalone}
\makeatletter
%% Utils
\def\pgfextractxy#1{\pgf@process{#1}\pgfgetlastxy}
\newcommand*\fpUnlessIf{
\edef\pgfmathresult{\fpeval{#1}}%
\ifnum\pgfmathresult=1 \expandafter\@gobble
\else\expandafter\@firstofone\fi}
\newcommand*\fpsetmacro{\edef#1{\fpeval{#2}}\typeout{#1 = #2}}
%% PGF
\def\pgfpointcurveatangle@#1#2#3#4#5{%
\def\@time{1}\fpsetmacro\@tan{tand(#1)}%
\pgfextractxy{#2}\@Ax\@Ay \pgfextractxy{#3}\@Bx\@By
\pgfextractxy{#4}\@Cx\@Cy \pgfextractxy{#5}\@Dx\@Dy
\fpsetmacro\@divisor{
-3*\@Bx*\@tan+3*\@Cx*\@tan-\@Dx*\@tan-\@Ax*\@tan -\@Ay+3*\@By-3*\@Cy+\@Dy}%
\fpUnlessIf{\@divisor==0}{% divide by 0?
\fpsetmacro\@divnosqrt{%% calculate dividend (no sqrt part)
-2*\@Bx*\@tan+\@Cx*\@tan+\@Ax*\@tan-\@Ay+2*\@By-\@Cy}%
\fpsetmacro\@divsqrt{%% calculate divident (sqrt part)
(-4*\@Bx*\@tan+2*\@Cx*\@tan+2*\@Ax*\@tan -2*\@Ay+4*\@By-2*\@Cy)^2
-4*(\@Bx*\@tan-\@Ax*\@tan+\@Ay-\@By)
*(3*\@Bx*\@tan-3*\@Cx*\@tan+\@Dx*\@tan-\@Ax*\@tan+\@Ay-3*\@By+3*\@Cy-\@Dy)}%
\fpUnlessIf{\@divsqrt<0}{% sqrt(neg)?
\fpsetmacro\@time{(.5*sqrt(\@divsqrt)+\@divnosqrt)/\@divisor}%
\fpUnlessIf{\@time>=0&&\@time<=1}{%
\fpsetmacro\@time{(-.5*sqrt(\@divsqrt)+\@divnosqrt)/\@divisor}}%
}}}
\def\pgfpointcurveatangle#1#2#3#4#5{%
\pgfpointcurveatangle@{#1}{#2}{#3}{#4}{#5}%
\pgfpointcurveattime{\@time}{#2}{#3}{#4}{#5}}
\def\pgftransformcurveatangle#1#2#3#4#5{%
\pgfpointcurveatangle@{#1}{#2}{#3}{#4}{#5}%
\pgftransformcurveattime{\@time}{#2}{#3}{#4}{#5}}
% TikZ
\newif\iftikz@angletimer
\def\tikz@timer@curve{%
\iftikz@angletimer
\pgftransformcurveatangle{\tikz@time@angle}{\tikz@timer@start}
{\tikz@timer@cont@one}{\tikz@timer@cont@two}{\tikz@timer@end}%
\else
\pgftransformcurveattime{\tikz@time}{\tikz@timer@start}
{\tikz@timer@cont@one}{\tikz@timer@cont@two}{\tikz@timer@end}%
\fi}%
\tikzset{
pos angle/.code=%
\edef\tikz@time@angle{#1}%
\ifx\tikz@time@angle\pgfutil@empty\else
\pgfmathsetmacro\tikz@time@angle{\tikz@time@angle}\fi
\tikz@angletimertrue,
pos/.append code=\tikz@angletimerfalse}
\makeatother
\usetikzlibrary{calc}
\tikzset{
cs/.pic={
\coordinate (-x) at (1,0) coordinate (-y) at (0,1) coordinate () at (0,0);},
pic cs/.style={shift={(#1)}, x={($(#1-x)-(#1)$)}, y={($(#1-y)-(#1)$)}}}
\begin{document}
\begin{tikzpicture}
\draw (0,6) -- (0,0) -- (9,0);
\draw[blue] (0,5) to[out=-5,in=100]
pic[pos angle=-30, sloped] (a) {cs} (8,0);
\draw[pic cs=a, red] (-2,0) -- (2,0);
\node[below left] at (a) {
\pgfmathanglebetweenpoints{\pgfpointanchor{a}{center}}
{\pgfpointanchor{a-x}{center}}
\pgfmathresult};
\end{tikzpicture}
\end{document}


## Output • I realize now that there's \fp_compare… which might be better than my “dirty” \fpUnlessIf but too late … Jan 11 at 23:12
• Woah! Impressive!! I feel a bit bad because with a similar amount of algebra, I could have just figured out the correct parametrization of the curve in my figure. But much appreciated! Jan 12 at 1:34