Bézier curves
The Bernstein basis
Definition · Bernstein polynomials
The Bernstein polynomials of degree \(n \ge 0\) are
For all other integers \(i\) we put \(b_{i,n} = 0\).
The polynomials are due to Bernstein (1912); Farouki (2012) surveys their history and properties, and The cubic Bernstein polynomials. shows the cubic ones. The proofs below use two identities of binomial coefficients.
Lemma · Binomial identities
Let \(n \ge 1\) and let \(i, j\) be integers, with \(\binom{m}{k} = 0\) for \(k < 0\) or \(k > m\). Then
and
Proposition · Bernstein basis
For \(n \ge 0\), the polynomials \(b_{0,n}, \ldots, b_{n,n}\) are a basis of the vector space of real polynomials of degree at most \(n\).
This is Theorem 1.1 of Floater (2025); Proofs proves it from scratch.
Proposition · Partition of unity
For every \(t \in [0, 1]\), \(b_{i,n}(t) \ge 0\) for all \(i\), and
Definition · Bézier curve
Let \(P_0, \ldots, P_n \in \mathbb{R}^d\). The Bézier curve of degree \(n\) with control points \(P_0, \ldots, P_n\) is
and its control polygon is the polyline \(P_0 P_1 \ldots P_n\).
The definition follows B{\'e}zier (1966) in the form of Farin (2002) and Prautzsch (2002).
Proposition · Endpoints
\(b_{i,n}(0) = 1\) if \(i = 0\) and \(0\) otherwise; \(b_{i,n}(1) = 1\) if \(i = n\) and \(0\) otherwise. Hence \(B(0) = P_0\) and \(B(1) = P_n\).
Corollary · Convex hull
Every point \(B(t)\), \(t \in [0, 1]\), lies in the convex hull of the control points \(P_0, \ldots, P_n\).
Proposition · Affine invariance
For every affine map \(A(x) = M x + v\) and every \(t \in [0, 1]\),
Transforming the curve is the same as transforming its control points.
Convex hull keeps a curve inside the region its control polygon spans, and Affine invariance is why the exporters and the Matplotlib adapter can move, scale or rotate a curve by moving its control points alone.
BernsteinBasis(int)(float)
BernsteinBasis(int).matrix(values)
The basis of one degree, in bezierkit.bezier.basis. Calling it at \(t\)
returns the values \(b_{i,n}(t)\) for \(i = 0, \ldots, n\) as an array;
matrix() returns one such row per parameter value.
Curves
BezierCurve(
points,
*,
evaluator=None
)
BezierCurve.linear(p0, p1)
BezierCurve.quadratic(
p0,
p1,
p2
)
BezierCurve.cubic(
p0,
p1,
p2,
p3
)
The degree \(n\).
The dimension \(d\).
The control points as a PointSet.
\(B(t)\) as a Point; a curve object is also callable, curve(t).
\(B\) at many parameters, as a PointSet.
The evaluation strategy (see below).
An immutable Bézier curve of any degree \(n \ge 0\) and dimension \(d \ge 1\),
over \([0, 1]\). points is a sequence of Points, a PointSet, a
\((n + 1) \times d\) array or a ControlPolygon; the control points must
share one dimension.
The operations on a curve, derivative(), split(), segment() and
reversed(), are the subject of Derivatives, reversal and subdivision.
Evaluation
de Casteljau's algorithm evaluates \(B(t)\) by repeated linear interpolation.
Definition · de Casteljau points
For a parameter \(t\) put \(P_i^{(0)} = P_i\) and, for \(r = 1, \ldots, n\),
Each round averages neighbouring points, so the polygon shrinks by one point; after \(n\) rounds one point is left (de Casteljau's algorithm at \(t = 0.4\).). The algorithm goes back to Paul de Casteljau's work at Citroën around 1959 M{\"u}ller (2024).
Theorem · de Casteljau
For \(0 \le r \le n\) and \(0 \le i \le n - r\),
In particular \(P_0^{(n)} = B(t)\).
The statement is Theorems 1.6 and 1.7 of Floater (2025).
Every intermediate point is a convex combination of the control points, so the algorithm never forms large cancelling terms; it is the numerically stable choice for high degrees. Its cost is \(O(n^2)\) per parameter.
DeCasteljauEvaluator
The default evaluation strategy, in bezierkit.bezier.evaluation: the
algorithm above, applied to a whole batch of parameters at once with
NumPy.
BernsteinEvaluator
Multiplies the Bernstein matrix by the control points. It is faster for low degrees and large batches but sums terms that can cancel at high degree. By de Casteljau both strategies compute the same polynomial.
from bezierkit import BezierCurve
from bezierkit.bezier.evaluation import BernsteinEvaluator
fast = BezierCurve(
curve.control_points, evaluator=BernsteinEvaluator()
)
# Point(coords=(2.0, 1.5)), as with the default
print(fast.at(0.5))
The repository's benchmarks/ directory compares the two on batches of 400,
10,000 and 100,000 parameter values.
Degree elevation
A curve of degree \(n\) is also a curve of degree \(n + 1\); the new control points are convex combinations of neighbouring old ones.
Lemma · Degree elevation identity
For \(n \ge 0\), \(0 \le i \le n\) and every \(t\),
Proposition · Degree elevation
Let \(B\) have control points \(P_0, \ldots, P_n\). For \(k = 0, \ldots, n + 1\) put
where \(P_{-1}\) and \(P_{n+1}\) may be chosen arbitrarily, since their coefficients are zero. Then
The curve and its parameterization are unchanged.
The formula is standard Farin (2002)Prautzsch (2002); the proof in Proofs is self-contained. Lines and quadratics as cubics applies it to lines and quadratics, the cases the package needs.