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
This is the coolest thing I’ve seen in a while. Thank you for writing it out, it has helped me out of a jam.
Thanks a lot! hapy you found interesting my math paper.
Check this other about
—
Quadratic bezier through three points
and the “equivalent quadratic bezier (theorem)”
Gabriel Suchowolski, December, 2012
http://microbians.com/?page=math&id=math-quadraticbezierthroughthreepoints
—