[Tex/LaTex] Macro to draw a parabola with pgf/TikZ

macrostikz-pgf

I have been trying to build a macro \parabola{...} to draw a parabola passing through 3 coordinates with TikZ but without success.

For example

\parabola{A}{B}{C}

would draw the parabola interpolating the (x,y) coordinates (A), (B) and (C). I would like also to specify the style of the curve, the plot domain, etc.

The main problem I found is that I cannot figure out how to obtain the value of a given coordinate in dimensionless form (given a certain unit).

Best Answer

Introduction

This is an old question, but all previous answers have limitations: the main one is that all use plot. And plot command produce multiple cubic curves. But to draw a parabola a single quadratic (cubic) curve is enough.

Some explanations

Any parabola can be drawn by a quadratic Bézier curve, and so by a cubic Bézier curve.
(A cubic curve with control points A,B,C,D draws a quadratic one iff AD=3BC.)

enter image description here

The "standard" parabola t(1-t) over [0,1] can be drawn by \draw (0,0) .. controls (1/3,1/3) and (2/3,1/3) .. (1,0);.

enter image description here

Every parabola between two points can be obtained by an affine transform from this "standard one". Using this we can define a style parabola through that use a single Bézier curve to draw the desired parabola. This style can be used with to or edge in the following way (A) to[parabola through={(B)}] (C).

The code

The definition of the parabola through is:

\makeatletter
\def\pt@get#1#2{
  \tikz@scan@one@point\pgfutil@firstofone#2\relax%
  \csname pgf@x#1\endcsname=\pgf@x%
  \csname pgf@y#1\endcsname=\pgf@y%
}
\tikzset{
  parabola through/.style={
    to path={{[x={(\pgf@xc,\pgf@yc)}, y=\parabola@y, shift=(\tikztostart)]
      -- (0,0) .. controls (1/3,1/3) and (2/3,1/3) .. (1,0) \tikztonodes}--(\tikztotarget)}
  },
  parabola through/.prefix code={
    \pt@get{a}{(\tikztostart)}\pt@get{b}{#1}\pt@get{c}{(\tikztotarget)}%
    \advance\pgf@xb by-\pgf@xa\advance\pgf@yb by-\pgf@ya%
    \advance\pgf@xc by-\pgf@xa\advance\pgf@yc by-\pgf@ya%
    \pgfmathsetmacro\parabola@y{(\pgf@yc-\pgf@xc/\pgf@xb*\pgf@yb)%
      /(\pgf@xb-\pgf@xc)*\pgf@xc}%
  }
}
\makeatother

Note: We can avoid \makeatletter/\makeatother and all @s by using let from the calc library.

We can use (A) to[parabola through={(B)}] (C):

  • in every case where the parabola exists, so when the three x-coordinates are different,
  • the point B can be outside the drawn are,
  • this can be part of a general path with nodes positioned on it.

Example 1:

\tikz\draw[help lines] (0,0) grid (4,3)
  (0,0) edge[parabola through={(3,2)},
    red,thick,fill=blue,fill opacity=.21] (4,1);

enter image description here

Example 2 (Full MWE):

\documentclass[tikz,border=7pt]{standalone}
\makeatletter
\def\pt@get#1#2{
  \tikz@scan@one@point\pgfutil@firstofone#2\relax%
  \csname pgf@x#1\endcsname=\pgf@x%
  \csname pgf@y#1\endcsname=\pgf@y%
}
\tikzset{
  parabola through/.style={
    to path={{[x={(\pgf@xc,\pgf@yc)}, y=\parabola@y, shift=(\tikztostart)]
      -- (0,0) .. controls (1/3,1/3) and (2/3,1/3) .. (1,0) \tikztonodes}--(\tikztotarget)}
  },
  parabola through/.prefix code={
    \pt@get{a}{(\tikztostart)}\pt@get{b}{#1}\pt@get{c}{(\tikztotarget)}%
    \advance\pgf@xb by-\pgf@xa\advance\pgf@yb by-\pgf@ya%
    \advance\pgf@xc by-\pgf@xa\advance\pgf@yc by-\pgf@ya%
    \pgfmathsetmacro\parabola@y{(\pgf@yc-\pgf@xc/\pgf@xb*\pgf@yb)%
      /(\pgf@xb-\pgf@xc)*\pgf@xc}%
  }
}
\makeatother
\begin{document}
  \begin{tikzpicture}
    \draw[help lines] (-1,-1) grid (3,3);
    % variations of the point "through"
    \foreach \y in {-1,-.9,...,1}
      \draw[green] (-1,1) node[black]{.}
        to[parabola through={(0,\y)}] node[black]{.}
          node[black,at end]{.} (1,.5);
    % variations of a boundary point
    \foreach \y in {1.5,1.7,...,3}
      \draw[purple] (-1,2) node[black]{.}
        to[parabola through={(0,2)}] node[black]{.}
          node[black,at end]{.} (1,\y);
    % variations of a point "trough" outside the drawn part
    \foreach \y in {-1,-0.5,...,3}{
      \draw[red,thick] (.5,1) node[black]{.}
        to[parabola through={(3,\y)}] node[black]{.}
          node[black,at end]{.} (2,1);
      \draw[dashed,blue] (.5,1) node[black]{.}
        to[parabola through={(2,1)}] node[black]{.}
          node[black,at end]{.} (3,\y);
    }
  \end{tikzpicture}
\end{document}

enter image description here

Compared to the built in parabola operation

TikZ provide a parabola path operation. But it is not very well designed :

  • the (0,0) parabola (1,1) is supposed to draw the parabola t^2 between 0 and 1. It draws a cubic curve that is close to this parabola but it is not exactly the same, actually it draws (0,0) .. controls (.5,0) and (0.8875,0.775) .. (1,1), but the exact curve is (0,0) .. controls (1/3,0) and (2/3,1/3) .. (1,1) (not clear why this curve is not used),
  • when used with bend option, it use two cubic curves to approximate the parabola, but only one is enough to draw the exact one,
  • when used with bend=<point> option, if you do not choose well the point the curve is not a parabola.

There is a situation where the original parabola is simpler to use (even if not exactly a parabola is drawn), when the bend (the extremal point) is at the start or at the end : (0,0) parabola (2,4) is simpler than (0,0) to[parabola through={(1,1)}] (2,4).

Related Question