Heya, Thanks for visiting!
edited

I've been playing around with Bezier curves and elliptical arcs for my on-going CNC plotter project. Instead of just using g-code commands which are standard on most CNC machines, I decided to support SVG path commands. This means supporting the linear moveTo(M, m) and lineTo(L, l) as well as curve(C, c, S, s -- Q, q, T, t)/arc(A, a) commands.

Here are a few articles explaining the SVG path commands:

In the interactive demos scattered throughout this article, you can set explicit values in the "Text input controls" flyout menu(on the left).

tl;dr, I just need a library

The JavaScript and C++ implementations are available on GitHub: SVG Curve Library.

I implemented the SVG path commands in JavaScript and ported it over to C++11 in order to run on the Teensy 3.0/3.1 (Arduino-like) microcontroller.

Bezier Curves

Bezier curves are a made up of a set of control points, and a start/end point which are sometimes categorized along with the control points. The controls points influence where the curve should go. A Bezier curve can have n number of control points but we will only go over the quadratic(1) and cubic(2) varieties. The math for Bezier curves is a simple parametric equation.

  • A quadratic Bezier curve, has only a single control (1)point/handle.
  • A cubic Bezier curve, has two control (2)points/handles.

Quadratic Bezier Math/Code:

Cubic Bezier Math/Code

Other Bezier Curve Links/Resources:

A small collection of links that helped me.

Elliptical Arcs

These were harder to implement for me, solely because of the amount of other information out there is very slim. I found the Implementation Notes page from the SVGWG to be most helpful.

Elliptical arcs are simply sub-sections(arcs) of an ellipse(oval).

Elliptical Arc Math/Code

Elliptical arcs a bit more complicated to work out because the point on elliptical arc equation is not in a nice endpoint parameterization format. We will need to convert this center parameterized equation to a endpoint parameterization. If you want to do this conversion on your own, there are some great instructions from SVGWG.

Beware: Matrix math, make sure to expand this expression correctly

  • cx, cy are the coordinates of the center of the ellipse
  • rx, ry are the radii of the ellipse (also known as its semi-major and semi-minor axes).
  • phi (φ) is the rotation of the ellipse from the x-axis
  • theta (θ) angle. The "actual" variable in the equation that determines where the point on the arc is.

Other Elliptical Arc Links/Resources:

A small collection of links that helped me.

Arc/Curve Length (Distance)

Calculate the arc length/total distance of a curve/arc. It also returns some other useful/debug information that we use in generateLinearCurve(...)(see below).

Usage:

// Resolution is the number of segments we use to approximate
var resolution = 25;

var quadBezierArcLengthResult = approximateArcLengthOfCurve(resolution, function(t) {
    return pointOnQuadraticBezierCurve(startPoint, controlPoint1, endPoint, t);
});

var ellipticalArcArcLengthResult = approximateArcLengthOfCurve(resolution, function(t) {
    return pointOnEllipticalArc(startPoint, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, endPoint, t);
});

console.log('Quad. Bezier arc length:', quadBezierArcLengthResult.arcLength);
console.log('Elliptical arc, arc length:', ellipticalArcArcLengthResult.arcLength);

Linear Curve/Arc

A challenge I faced, was dealing with t not being linear or having an even-distribution. This means that if you just sample t at a constant interval, you will have more samples bunched up in the curvier parts.

This at first seems good because you need the most detail in corners in order to accurately represent the curve, but it also leaves you with an unknown precision/error. So I decided to implement a linear curve function in order to get a known degree of accuracy. I am not sure if this is actually better or worse for accuracy/precision. The linear curve function is also useful in games or animations where you want the object to travel along a curve at a constant rate. This will work for any pointOnThing function.

See also: Section 20. Tracing a curve at fixed distance intervals from A Primer on Bézier Curves

generateLinearCurve(...) will return a new function where you can pass in a t [0, 1] and get a point on the curve back. Since t will be linear with this, 0.5 is halfway along the curve, distance wise.

Usage:

// Resolution is the number of segments we use to approximate
var resolution = 25;

var linearQuadBezier = generateLinearCurve(resolution, function(t) {
    return pointOnQuadraticBezierCurve(startPoint, controlPoint1, endPoint, t);
});

var linearEllipticalArc = generateLinearCurve(resolution, function(t) {
    return pointOnEllipticalArc(startPoint, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, endPoint, t);
});

// Pass in t [0, 1]
// Returns a point along the curve
var pt1 = linearQuadBezier(0.5);
var pt2 = linearEllipticalArc(0.5);