Update an Asymptote answer (19 March 2022) simple and direct way, both 2D and 3D curves. TikZ considers tangents/normals as decorations; meanwhile Asymptote treat them as true paths.
For 2D curve:
// http://asymptote.ualberta.ca/
unitsize(1cm);
path mypath=(0,0) ..controls (0,0)+5dir(70) and (8,3)+5dir(-120) .. (8,3);
draw(mypath);
real t=.2;
pair P=point(mypath,t);
pair Pt=dir(mypath,t); // tangent vector at P
pair Pn=rotate(90)*Pt; // normal vector at P
draw(mypath);
draw(P-2Pt--P+4Pt,red);
draw(P-2Pn--P+2Pn,blue);
dot(P);
shipout(bbox(5mm,invisible));
For 3D curve: Note that triple perp(triple v)
return a unit vector perpendicular to a given unit vector v
.
In the following figure, the tangent line is in red, the normal plane is in green.
// http://asymptote.ualberta.ca/
import three;
unitsize(2cm);
path3 g=(1,0,0)..(0,1,1)..(-1,0,0)..(0,-1,1)..cycle;
real t=.3;
triple P=point(g,t);
triple Pt=dir(g,t); // the tangent vector at P
triple Pn1=perp(Pt);
triple Pn2=cross(Pt,Pn1);
//dot(P+Pn1^^P+Pn2,red); // 2 points on the normal plane at P
path3 Pnormal=plane(1.5Pn1,1.7Pn2,P-.8Pn1-.7Pn2); // the normal plane at P
draw(g);
draw(P-2Pt--P+3Pt,red);
draw(Pnormal,blue);
draw(surface(Pnormal),lightgreen+opacity(.5));
Old answer
C.F.G.'s trick is nice! First I modify a bit his trick: [inner ysep=.5pt]
to control thickness of tangent, and adding [rotate=90]
to get normal segment. Not that with this trick, there is a restriction: both tangent and normal segments must have the same midpoint at underlying point on the curve.
Then I remove this restriction using pic
, also with handy option [sloped]
. Now drawing tangents and normals seem to be done, except predefined curves like ellipse
, circle
, .... However, we can use their parameterization expressions, and plot directly ^^
\documentclass[tikz,border=5mm]{standalone}
\begin{document}
% 1st way with node (based on C.F.G's trick)
\begin{tikzpicture}
\draw (0,0) ..controls +(70:5) and +(-120:5) .. (8,3)
% for tangent
node[sloped,inner xsep=1.5cm,inner ysep=.5pt, fill,pos=.12,red] (P) {}
% for normal (just add [rotate=90])
node[rotate=90,sloped,inner xsep=1.5cm,inner ysep=.5pt,fill,pos=.12,blue] {}
;
\fill (P) circle(2pt) node[above]{P};
\end{tikzpicture}
% 2nd way with pic (more handy)
\begin{tikzpicture}[tangent/.pic={
\draw (-1.5,0)--(2.5,0);
}]
\draw (0,0) ..controls +(70:5) and +(-120:5) .. (8,3)
coordinate[pos=.87] (Q)
% for tangent
pic[pos=.87,sloped,cyan,thick]{tangent}
% for normal (just add [rotate=90])
pic[pos=.87,sloped,rotate=90,brown,thick]{tangent}
;
\fill (Q) circle(2pt) node[above]{Q};
\end{tikzpicture}
\end{document}
Update As AndreC suggested, I make pic
named segment
with 3 parameters: angle #1 left #2 right #3, where angle 0
is for tangent, angle 90
is for normal, #2 and #3 are for length of segment to 2 endpoints of segment from underlying point on the curve. Option on thickness can be put later when using pic
with line width
option.
\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\tikzset{pics/segment/.style args=
{angle #1 left #2 right #3}{
code={\draw[rotate=#1] (180:#2)--(0:#3);}}}
\begin{tikzpicture}
\def\mecurve{(0,0) ..controls +(60:6) and +(-120:6) .. (8,2)}
\draw \mecurve;
\foreach \t in {.1,.5,...,1}
\path \mecurve
% for tangent {angle 0}
pic[pos=\t,sloped,cyan,thick]
{segment=angle 0 left 2 right 1.5}
% for normal {angle 90}
pic[pos=\t,sloped,orange,thick]
{segment=angle 90 left 1 right 2}
node[pos=\t]{$\bullet$};
\end{tikzpicture}
\end{document}
As an applicaton, we can mark along curve (may use when the curve has quite small slope).
\foreach \t in {0,.025,...,1}
\path \mecurve
pic[pos=\t,sloped,orange,line width=1pt]
{segment=angle 90 left 2mm right 2mm};
\end{tikzpicture}
pos=0.695
, and draw a line over them and extend the line.