I was prototyping something and I needed to draw a curve with some thickness. It wasn’t just the case of increasing the thickness of the stroke, I wanted to find the contour of a curve, to draw two new curves around one in the center. After some research, I learnt that the correct term for that is **parallel curve** or **offset curve**.

The task turned out to be not as simple as I thought. After some failed attempts I found the solution in a paper by Gabriel Suchowolski entitled ‘Quadratic bezier offsetting with selective subdivision‘. The recipe is there, but I was missing an open source implementation so I decided to write one.

In this post I present a step by step process and at the end an interactive version written in Javascript.

How to draw an offset curve:

Start with 3 points.

Draw a quadratic curve using `p1`

and `p2`

as anchors and `c`

as the control point.

Get the vectors between these points.

`v1 = c - p1`

v2 = p2 - c

Find the vector perpendicular to `v1`

and scale it to the width (or thickness) of the new curve.

Add the new temporary vector to `p1`

to find `p1a`

, then subtract from `p1`

it to find `p1b`

.

Do the same with `c`

to find `c1a`

and `c1b`

.

Repeat the same process with `v2`

to find the points on the other side.

Find vectors between the new points. These are parallel to `v1`

and `v2`

and offset by the given thickness.

The intersection points of these vectors are the new control points `ca`

and `cb`

.

Draw a curve from `p1a`

to `p2a`

with control point at `ca`

.

Draw another curve from `p1b`

to `p2b`

with control point at `cb`

.

This method works only when the angle between `v1`

and `v2`

is wide (bigger than 90 degrees), **it doesn’t work for sharp angles**.

For angles smaller than 90 degrees it is necessary to split the curve. In fact the curve could be split several times, the more the better the precision of the offset curve. To do it only at 90 degrees is fast and the result is not too bad.

The curve needs to be split at `t`

, which is the closest point to `c`

in the curve. The technique to find `t`

has been described in the paper I mentioned before. It requires solving a third degree polynomial like this `ax3+bx2+cx+d=0`

The equation returns a number between 0 and 1 that can be plotted in the curve to find `t`

.

Find the tangent of `t`

and the points `t1`

and `t2`

where it intersects `v1`

and `v2`

.

Create a new vector perpendicular to the tangent of `t`

, scale it to the given thickness and find `qa`

and `qb`

. This vector splits the original curve at `t`

.

Add the tangent of `t`

to `qa`

and `qb`

and find the points where it intersects the offset vectors.

These are all the points needed to draw an offset curve. All the others that were created in the process can be removed for clarity.

Draw a curve with anchors at `p1a`

and `qa`

with the control point at `q1a`

.

Repeat the process for all the new points to get the offset curve.

—

Here is an interactive version. Drag the gray dots to change the curve.

See the Pen VYEWgY by Bruno Imbrizi (@brunoimbrizi) on CodePen.

Thanks to:

– Gabriel Suchowolski (aka @microbians) for his paper

– toxiclibs for the really handy Vec2D and Line2D classes

– Professor Eric Schechter for The Cubic Formula