Overview

Approximating bezier curves by circular arcs, in spite of how useless it sounds regarding modern drawing APIs, has (at least) one raison d’etre. The G-Code language used by most CNC machines, and also adopted by most 3D printers, can deal with linear interpolation (lines) and circular interpolation (circular arcs) only.

I have been working recently on a simple Haskell SVG to G-Code converter, and I realized, that in spite of the simplicity of the algorithm at the end, how confusing the subject is (partly because of some not very well written research paper(s) delivered by google search on the subject).

The algorithm explained in this post is implemented in C# and can be found at my GitHub repository. The C# code is only for illustrating the algorithm, later on it will be integrated with my Haskell SVG to G-Code project.

The article assumes that you understand basic algebra and geometry, complex numbers, etc…

Bezier curves

Cubic bezier curve

I assume that you already familiar with bezier curves, here I want to introduce only the notations used in this post. We restrict ourselves to cubic bezier curves (it is not a real limitation, any quadratic bezier curve can be straightforwardly converted to a cubic bezier curve). P1 denotes the start point, P2 the end point, and C1, C2 the control points of the start and end points, respectively. Because it is important for the algorithm, please remember that the line denoted by P1 and C1 is the tangent at P1, and, similarly, the line denoted by P2 and C2 is the tangent at P2.

Biarcs

Biarcs

A biarc is a pair of circular arcs (= two arcs) which have the same tangent at the connection point they meet. We will use one biarc to approximate a bezier segment which has no inflection point. A traditional biarc approximation task has four parameters: a start point, an end point, and the tangents at these points. Using these four parameters, however, is not enough to uniquely identify a biarc, we need one more parameter: this can be e.g. the connection point of the arcs or the tangent at the connection point.

Before we start

This algorithm works on bezier curves without any inflection points. Thus, as a preliminary step, the inflection points must be found and the curve must be split (the parts will be approximated one by one). It is a simple task though. We just have to find the points where the second derivative of the parametric equation of the bezier curve becomes zero. It is a quadratic equation, so that can happen at no more than two points. Obviously, we will be interested in the real (not complex) solutions only and only which are in the [0,1] interval. For the details please contact with the implementation.

Approximation of Bezier curves by circular arcs

The biarc fitting

We have a simple cubic bezier curve at this point, and we want to approximate it with a biarc. For that we need one more parameter. Some research suggests [1] that in the case of bezier curves, the connection point of the arcs of the biarc should be the incenter point of the triangle denoted by the points P1, P2, and V, where V is the intersection point of the tangents at P1 and P2. Let’s call this incenter point G.

For illustration, see the image at the bottom of the article. Black color is related to the original bezier curve, green color is related to the incenter point, red color is related to the approximation biarc, and yellow color is related to the arc computations as it is explained in the following paragraph.

The next step is to find the two circles on which the arcs lie. We have three clues per circle. Circle 1: its two points P1 and G, and the tangent at P1. Circle 2: its two points G and P2, and the tangent at P2.

To be done, we need to find C1 and C2, the center points of these circles.

It is simple. We know the tangent at P1. C1 lies on the line which is perpendicular to this tangent and goes through P1, let’s denote it by P1C. If we take the section between P1 and G, its perpendicular bisector (EC1) intersects with P1C at C1. The same method can be used to find C2.

Estimate the error and go recursive

Obviously, as you can also see on the illustration, most of the time the approximation is not close enough. Thus, after approximation, the error must be estimated, and if it is out of the tolerance range the bezier curve must be split, then the two new bezier curves must be approximated recursively until you reach an acceptable deviation. In my implementation I just simply check the distance at a certain number of points along the curve and split if the maximum deviation is not acceptable (the bezier curve is split where the deviation is the maximum as suggested by [2]). This is certainly not fast, but acceptable for my purposes.

For what project do you want to use bezier approximation?