robotics : Ss9rw

Arcs and Bézier Curves

Mathematics and code for evaluating arcs and bézier curves.

Jan 27, 2024
Updated Feb 3, 2024

Introduction

This post will cover the mathematics and code for evaluating arcs and bézier curves. Arcs can prove to be useful for the obvious case when you want a pure arc trajectory. Bézier curves may prove useful tracing fonts which are usually described with bézier curves.

As always, the code presented in this post is ANSI C. Not every sub-routine and datatype will be covered in this post. For that, visit the repository at github.com/fullnitrous/arm, all the relevant code is found under /src/core/.

Arcs

An arc is considered to be part of, or a circle, it is defined to span from the vectors \(\vec{v_0}\) to \(\vec{v_1}\) and a center point \(\vec{c}\). Optionally, the arc can overwrite the angle between \(\vec{v_0}\) and \(\vec{v_1}\) to span by angle \(a_{\text{offset}}\).

Given \(\vec{v_0}\) \(\vec{v_1}\), \(\vec{c}\) and \(a_{\text{offset}}\), the vectors \(\vec{u}\) and \(\vec{v}\) are defined by the following equations.

\[ \begin{aligned} \vec{u} &= \vec{v_0} - \vec{c} \\ \vec{v} &= \vec{v_1} - \vec{c} \end{aligned} \]

The following conditions must be met for \(\vec{u}\) and \(\vec{v}\) in order for \(\vec{v_0}\), \(\vec{v_1}\) and \(\vec{c}\) to specify a valid arc.

  • \(|\vec{u}| = |\vec{v}|\)
    • The lengths of vectors \(\vec{u}\) and \(\vec{v}\) must be the same because points on the arc must have the same radius (distance from \(\vec{c}\)).
  • \(|\vec{u} \times \vec{v}| > 0\)
    • The magnitude of the cross product between \(\vec{u}\) and \(\vec{v}\) must be non-zero, otherwise the vectors do not specify a plane for the arc.

If the conditions are met, then the arc is defined by its normal vector \(\vec{n}\), center point \(\vec{c}\) and radius vector (initial point) \(\vec{r}\) and angle \(a\).

\[ \begin{aligned} \vec{n} &= \frac{\vec{u} \times \vec{v}}{|\vec{u} \times \vec{v}|} \\ \vec{r} &= \vec{u} \\ a &= \begin{cases} a_{\text{offset}} & \text{ if use $a_{\text{offset}}$} \\ acos \left ( \frac{\vec{u}\cdot\vec{v}}{|\vec{u}|\cdot|\vec{v}|} \right ) & \text{else} \\ \end{cases} \end{aligned} \]

The type arc_t stores all the aforementioned parameters.

Then using the intrpl_arc routine given the vectors v0, v1, c and optionally angle offset ao (set to 0.0 if not used). Will save the computed arc parameters to arc.

The arc interpolation function \(A(t), \space t \in [0,1]\) is defined as the following.

\[ A(t) = \vec{r}cos(ta) + (\vec{r}\cdot\vec{n})\vec{n}(1-cos(ta))+(\vec{n}\times\vec{r})sin(ta) \]

This function is derived from a formula I used in the post about Three Dimensional Rocket Simulation. Essentially how it works is by rotating a point around an axis which essentially creates an arc.

Which is programmed as the function eval_arc.

Arc plot with \vec{v_0}=(1,0,0), \vec{v_1}=(0,0,1), c=(0,0,0), a_\text{offset}=\frac{4}{3}\pi
Arc plot with \(\vec{v_0}=(1,0,0)\), \(\vec{v_1}=(0,0,1)\), \(c=(0,0,0)\), \(a_\text{offset}=\frac{4}{3}\pi\)

Bézier

A bézier curve \(B(t)\) is defined by its degree \(n\) and a set of \(n+1\) control points \(P_i\).

\[ \begin{aligned} B(t) &= \sum_{i=0}^{n}b_{i,n}P_i, \quad t \in [0,1] \\ b_{i,n}(t) &= \begin{pmatrix} n \\ i \end{pmatrix} t^i(1-t)^{n-i} \\ \begin{pmatrix}n\\i\end{pmatrix} &= \frac{n!}{i!(n-i)~} \end{aligned} \]

Its first and second derivatives are.

\[ \begin{aligned} B'(t) &= n\sum_{i=0}^{n-1}b_{i,{n-1}}(P_{i+1}-P_i) \\ B''(t) &= (n-1)n\sum_{i=0}^{n-2}b_{i,{n-2}}(P_{i+2}-2P_{i+1}+P_i) \\ \end{aligned} \]

Binomial Coefficient Lookup Table

Since bézier curves are a linear combination of Bernstein Basis Polynomials which are defined by binomial coefficients, a lookup table for the coefficients is preffered for faster evaluation of the curve.

A maximum degree for the bézier curve EVAL_BEZIER_MAX_DEG is defined and the size of the lookup table is defined by this.

The function eval_bezier_lut_init initializes the global lookup table.

Helper function __eval_fac for calculating the factorial of a number.

Helper function __eval_bico to calculate the binomial coefficient.

Then finally the function eval_bico for evaluating the binomial coefficient for n and i. If its possible to find the coefficient with the lookuptable then it simply does a lookup, otherwise it evaluates it using __eval_bico.

The function eval_bernstein_basis for evaluating the Bernstein basis polynomial \(b_{i,n}(t)\) simply follows by the formula.

Then finally the functions eval_bezier and __eval_bezier_fast are used in order to evaluate the curve. The first function calculates the derivatives where the second only calculates the position.

Bézier curve, n=3, P_0=(0,0,0), P_1=(0, 0.5, 1), P_2(1,0,1), P_3=(1,0.5,2). Control polygon shown in dashed line.
Bézier curve, \(n=3\), \(P_0=(0,0,0)\), \(P_1=(0, 0.5, 1)\), \(P_2(1,0,1)\), \(P_3=(1,0.5,2)\). Control polygon shown in dashed line.