...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
#include <boost/math/interpolators/bezier_polynomials.hpp> namespace boost::math::interpolators { template<RandomAccessContainer> class bezier_polynomial { public: using Point = typename RandomAccessContainer::value_type; using Real = typename Point::value_type; using Z = typename RandomAccessContainer::size_type; bezier_polynomial(RandomAccessContainer&& control_points); inline Point operator()(Real t) const; inline Point prime(Real t) const; void edit_control_point(Point cont & p, Z index); RandomAccessContainer const & control_points() const; friend std::ostream& operator<<(std::ostream& out, bezier_polynomial<RandomAccessContainer> const & bp); }; }
Bézier polynomials are curves smooth curves which approximate a set of control points. They are commonly used in computer-aided geometric design. A basic usage is demonstrated below:
std::vector<std::array<double, 3>> control_points(4); control_points[0] = {0,0,0}; control_points[1] = {1,0,0}; control_points[2] = {0,1,0}; control_points[3] = {0,0,1}; auto bp = bezier_polynomial(std::move(control_points)); // Interpolate at t = 0.1: std::array<double, 3> point = bp(0.1);
The support of the interpolant is [0,1], and an error message will be written if attempting to evaluate the polynomial outside of these bounds. At least two points must be passed; creating a polynomial of degree 1.
Control points may be modified via edit_control_point
,
for example:
std::array<double, 3> endpoint{0,1,1}; bp.edit_control_point(endpoint, 3);
This replaces the last control point with endpoint
.
Tangents are computed with the .prime
member function, and the control points
may be referenced with the .control_points
member function.
The overloaded operator << is disappointing: The control points are simply printed. Rendering the Bezier and its convex hull seems to be the best "print" statement for it, but this is essentially impossible in modern terminals.
Do not confuse the Bezier polynomial with a Bezier spline. A Bezier spline
has a fixed polynomial order and subdivides the curve into low-order polynomial
segments. This is not a spline! Passing n
control points to the bezier_polynomial
class creates a polynomial of degree n-1, whereas a Bezier spline has a fixed
order independent of the number of control points.
Requires C++17 and support for threadlocal storage.
The following performance numbers were generated for evaluating the Bezier-polynomial. The evaluation of the interpolant is 𝑶(N^2), as expected from de Casteljau's algorithm.
Run on (16 X 2300 MHz CPU s) CPU Caches: L1 Data 32 KiB (x8) L1 Instruction 32 KiB (x8) L2 Unified 256 KiB (x8) L3 Unified 16384 KiB (x1) --------------------------------------------------------- Benchmark Time CPU --------------------------------------------------------- BezierPolynomial<double>/2 9.07 ns 9.06 ns BezierPolynomial<double>/3 13.2 ns 13.1 ns BezierPolynomial<double>/4 17.5 ns 17.5 ns BezierPolynomial<double>/5 21.7 ns 21.7 ns BezierPolynomial<double>/6 27.4 ns 27.4 ns BezierPolynomial<double>/7 32.4 ns 32.3 ns BezierPolynomial<double>/8 40.4 ns 40.4 ns BezierPolynomial<double>/9 51.9 ns 51.8 ns BezierPolynomial<double>/10 65.9 ns 65.9 ns BezierPolynomial<double>/11 79.1 ns 79.1 ns BezierPolynomial<double>/12 83.0 ns 82.9 ns BezierPolynomial<double>/13 108 ns 108 ns BezierPolynomial<double>/14 119 ns 119 ns BezierPolynomial<double>/15 140 ns 140 ns BezierPolynomial<double>/16 137 ns 137 ns BezierPolynomial<double>/17 151 ns 151 ns BezierPolynomial<double>/18 171 ns 171 ns BezierPolynomial<double>/19 194 ns 193 ns BezierPolynomial<double>/20 213 ns 213 ns BezierPolynomial<double>/21 220 ns 220 ns BezierPolynomial<double>/22 260 ns 260 ns BezierPolynomial<double>/23 266 ns 266 ns BezierPolynomial<double>/24 293 ns 292 ns BezierPolynomial<double>/25 319 ns 319 ns BezierPolynomial<double>/26 336 ns 335 ns BezierPolynomial<double>/27 370 ns 370 ns BezierPolynomial<double>/28 429 ns 429 ns BezierPolynomial<double>/29 443 ns 443 ns BezierPolynomial<double>/30 421 ns 421 ns BezierPolynomial<double>_BigO 0.52 N^2 0.51 N^2
The Casteljau recurrence is indeed quadratic in the number of control points, and is chosen for numerical stability. See Bezier and B-spline Techniques, section 2.3 for a method to Hornerize the Berstein polynomials and perhaps produce speedups.
The Point
type must satisfy
certain conceptual requirements which are discussed in the documentation of
the Catmull-Rom curve. However, we reiterate them here:
template<class Real> class mypoint3d { public: // Must define a value_type: typedef Real value_type; // Regular constructor--need not be of this form. mypoint3d(Real x, Real y, Real z) {m_vec[0] = x; m_vec[1] = y; m_vec[2] = z; } // Must define a default constructor: mypoint3d() {} // Must define array access: Real operator[](size_t i) const { return m_vec[i]; } // Must define array element assignment: Real& operator[](size_t i) { return m_vec[i]; } private: std::array<Real, 3> m_vec; };
These conditions are satisfied by both std::array
and
std::vector
.