Smooth Bézier Spline Through Prescribed Points

Posted on June 17th, 2012
Previous Article :: Next Article

In this article we show you how to generate a smooth spline through a set of prescribed points. The article also contains an interactive SVG demo that allows you to try this in real time. This demo may not work if you are reading this post through email or on a mobile device. In that case, please visit the article from your computer browser.

Introduction

In scientific computing, we often need to construct a line that passes through a set of prescribed controlled points, or knots. A common example is mesh generation. Even if we utilize a mesh-free method, we need to somehow prescribe the outer extent for the problem. This is typically accomplished by specifying boundary splines (the counterpart in 3D are patches).

Bézier Splines

Spline is a collection of polygonal segments. The segments can be linear, quadratic, cubic, or even higher order polynomials. In this article we derive the equations needed to draw a smooth curve through a set of control points using the cubic Bézier polynomial. Wikipedia has a very nice article on Bézier curves that includes animations that show how these polynomials work.

The cubic Bézier curve is given by
\mathbf{B}(t)=(1-t)^3\mathbf{P}_0 + 3(1-t)^2t\mathbf{P}_1 + 3(1-t)t^2\mathbf{P}_2 + t^3\mathbf{P}_3, \quad t\in [0,1]
this can be rewritten as
\mathbf{B}(t)=(1-t)^3\mathbf{P}_0 + 3(t-2t^2+t^3)\mathbf{P}_1 + 3(t^2-t^3)\mathbf{P}_2 + t^3\mathbf{P}_3

In this definition, the points 0 and 3 correspond to the end points (the knots). The other two points are control points that determine the shape of the curve. The curve does not in general pass through these points. Following the steps outlined on codeproject.com, we want the first and second derivatives to be continuous across the spline boundary. This will give us two equations (one for each derivative) at each spline interface which we use to find the control points.

The first derivative is given by
\mathbf{B}'(t) = -3(1-t)^2 \mathbf{P}_0 + 3(1-4t+3t^2)\mathbf{P}_1 + 3(2t-3t^2)\mathbf{P}_2 +3t^2\mathbf{P}_3
At the left boundary of an “i-th” segment we can write
\mathbf{B}'_i(0)=\mathbf{B}'_{i-1}(1)
or
-3\mathbf{P}_{0,i}+3\mathbf{P}_{1,i}=-3\mathbf{P}_{2,i-1}+3\mathbf{P}_{3,i-1}
Now, since the curve is continuous, \mathbf{P}_{0,i}=\mathbf{P}_{3,i-1}=\mathbf{K}_i, the “i-th” knot point, and we can simplify this expression as (1)
2\mathbf{K}_i=\mathbf{P}_{1,i}+\mathbf{P}_{2,i-1} \quad\quad\quad(1)

We also want the second derivative to be continuous. The second derivative is given by
\mathbf{B}''(t)= 6(1-t)\mathbf{P}_0+3(-4+6t)\mathbf{P}_1+3(2-6t)\mathbf{P}_2+6t\mathbf{P}_3
at the boundary we have
6 \mathbf{P}_{0,i}-12\mathbf{P}_{1,i}+6\mathbf{P}_{2,i}=6\mathbf{P}_{1,i-1}-12\mathbf{P}_{2,i-1}+6\mathbf{P}_{3,i-1}
Simplifying and taking into account the shared knot point, we get equation (2),
-2\mathbf{P}_{1,i}+\mathbf{P}_{2,i}=\mathbf{P}_{1,i-1}-2\mathbf{P}_{2,i-1} \quad\quad\quad(2)

The equations (1) and (2) are defined only at the internal knots – places where two segments come together. Mathematically, this means that we have 2(n-1) equations for 2n unknowns. In order to close the system, we prescribe two more natural boundary conditions, \mathbf{B}''_0(0)=0 and \mathbf{B}''_{n-1}(1)=0. In other words, the spline becomes linear at the end points. These two remaining equations (3) and (4) are
\mathbf{K}_0-2\mathbf{P}_{1,0}+\mathbf{P}_{2,0}=0 \quad\quad\quad(3)
and
\mathbf{P}_{1,n-1}-2\mathbf{P}_{2,n-1}+\mathbf{K}_n=0 \quad\quad\quad(4)

We can further simplify this system by substituting (1) into (2) to obtain
\mathbf{P}_{1,i-1}+4\mathbf{P}_{1,i}+\mathbf{P}_{1,i+1}=4\mathbf{K}_i+2\mathbf{K}_{i+1} \quad\quad i\in[1,n-2].
On the boundary nodes we have from (3) and (4)
2\mathbf{P}_{1,0}+\mathbf{P}_{1,1}=\mathbf{K}_0+2\mathbf{K}_1
and
2\mathbf{P}_{1,n-2}+7\mathbf{P}_{1,n-1} = 8\mathbf{K}_{n-1}+\mathbf{K}_n

This is a tri-diagonal system for P_1 which we can solve using the Thomas algorithm. Once we determine \mathbf{P}_1, we obtain \mathbf{P}_2 from equations (1) and (4),
\mathbf{P}_{2,i}=2\mathbf{K}_{i}-\mathbf{P}_{1,i} \quad\quad i\in [0,n-2]
and
\mathbf{P}_{2,n-1}= (1/2)(\mathbf{K}_n+\mathbf{P}_{1,n-1})

Interactive Demo

Below you will find an interactive demo that implements this algorithm. If everything loaded fine, you should see four yellow circles connected by a smooth line composed of three differently colored segments. You can move any of the circles with the mouse. If you don’t see the circles, cannot move them, or the spline is not smooth, first try reloading the page. If that still fails, try a different browser. The demo worked for me with Chrome, Firefox, and Internet Explorer 9 under Windows. I couldn’t get it working on my Android phone (no graphics rendered) and a friend reported it didn’t work on iPad either. If you know how to get the demo working on these mobile devices, please let me know by leaving a comment below.

This demo uses SVG and Javascript (You can find a TON of SVG examples on Prof. Dailey’s website. Another good resource is the SVG tutorial on “Peter’s Website”). SVG (Scalable Vector Graphics) is one of two ways you can get interactive graphics in your website (the other being HTML5′s <canvas> element). Of these two, SVG seems to be much more versatile. There are even free vector graphics programs out there, such as Inkscape, which save the images in the SVG format. The three lines connecting the circles are defined using SVG’s Bézier cubic path syntax. Whenever you drag a circle, the code recomputes the control points and updates the path definition. Your browser then automatically repaints the view. Pretty nifty! You will find the complete code below.

Source Code

You can download the source code here: circles.svg. The image is embedded using the <embed> tag. You can also see just the Javascript: bezier-spline.js (this code is embedded inside the SVG).

As a side note, it took me way too long to get the code running. Javascript is different from Java in that, among other things, it does not define variable types. Initially, the code was using

for (i=0;i<4;i++)
{
    x[i]=V[i].getAttributeNS(null,"cx")
    y[i]=V[i].getAttributeNS(null,"cy")
}

to obtain the coordinates of the four control points. These were then passed to another function which performed the actual computation. The problem was, this actually resulted in x[i] being a string and not an integer. Javascript did not complain about this. Instead, where required it automatically converted the string to an int. However, the plus operator resulted in string concatenation instead of addition of two numbers. For example, for x[i]="50", x[i]+30 resulted in 5030 instead of the expected value of 80. For the longest time I was getting really funky results and could not figure out why despite rechecking my math multiple times. I finally found the bug by stepping through the code with the Javascript debugger that comes with Chrome. The simple fix was to force the conversion to integer,

for (i=0;i<4;i++)
{
    /*use parseInt to convert string to int*/
    x[i]=parseInt(V[i].getAttributeNS(null,"cx"))
    y[i]=parseInt(V[i].getAttributeNS(null,"cy"))
}

Also make sure to checkout the interactive online mesh generator follow up article.

Did you find this article useful? If so, please share the link with your friends, coworkers, and colleagues. And don't forget to subscribe to the newsletter and follow us on Google+ and Twitter. To find out more about how we can assist you on your project, please see the request for proposal page.

5 comments to “Smooth Bézier Spline Through Prescribed Points”

  1. June 18, 2012 at 7:06 am

    You will also find a lot of information on Bezier splines, including interactive HTML5 canvas demos at http://processingjs.nihongoresources.com/bezierinfo/

  2. June 19, 2012 at 7:32 am

    I also just found out this doesn’t work in Firefox 10 due to a bug that got fixed in FF11. In 10, the spline doesn’t update since the code to svg.getElementById(‘id’) returns an exception, “Component returned failure code: 0×80004001 (NS_ERROR_NOT_IMPLEMENTED) [nsIDOMSVGSVGElement.getElementById]”

    Update 6/20/2012: I modified the code to create the SVG elements dynamically. This also eliminated the need for the getElementById call so the new version should work in FF10 as well.

  3. Oswald
    June 20, 2012 at 1:12 pm

    The Bezier curve renders on my Asus Prime tablet with Android ICS (it will probably work on my iPad3 as well), and I did get some touch-related functionality working (but only partially).

    Btw, you might enjoy some of this SVG eye-candy:
    https://code.google.com/p/svg-filters-graphics/

Leave a Reply

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> In addition, you can use$latex ...$ to include equations.