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:

- SVG Basics—Creating Paths With Line Commands by Steven Bradley
- SVG Basics—Creating Paths With Curve Commands by Steven Bradley

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

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 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.

A small collection of links that helped me.

- Bézier curve on Wikipedia
- Tracing a curve at fixed distance intervals
- Moving ships between two planets along a bezier, missing some equations for acceleration
- Quadratic Bezier curve length
- Arc Length of Bézier Curves
- Computing Intersections Between a Cubic Bezier Curve and a Line

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 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.

A small collection of links that helped me.

- SVGWG Implementation Requirements
- Bounding Box of an SVG Elliptical Arc
- svgsalamander Arc.java source
- Apache xmlgraphics ExtendedGeneralPath.java source

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);

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);