how to draw smooth curve through N points using javascript HTML5 canvas?

Homan picture Homan · Aug 14, 2011 · Viewed 115.4k times · Source

For a drawing application, I'm saving the mouse movement coordinates to an array then drawing them with lineTo. The resulting line is not smooth. How can I produce a single curve between all the gathered points?

I've googled but I have only found 3 functions for drawing lines: For 2 sample points, simply use lineTo. For 3 sample points quadraticCurveTo, for 4 sample points, bezierCurveTo.

(I tried drawing a bezierCurveTo for every 4 points in the array, but this leads to kinks every 4 sample points, instead of a continuous smooth curve.)

How do I write a function to draw a smooth curve with 5 sample points and beyond?

Answer

Homan picture Homan · Aug 14, 2011

The problem with joining subsequent sample points together with disjoint "curveTo" type functions, is that where the curves meet is not smooth. This is because the two curves share an end point but are influenced by completely disjoint control points. One solution is to "curve to" the midpoints between the next 2 subsequent sample points. Joining the curves using these new interpolated points gives a smooth transition at the end points (what is an end point for one iteration becomes a control point for the next iteration.) In other words the two disjointed curves have much more in common now.

This solution was extracted out of the book "Foundation ActionScript 3.0 Animation: Making things move". p.95 - rendering techniques: creating multiple curves.

Note: this solution does not actually draw through each of the points, which was the title of my question (rather it approximates the curve through the sample points but never goes through the sample points), but for my purposes (a drawing application), it's good enough for me and visually you can't tell the difference. There is a solution to go through all the sample points, but it is much more complicated (see http://www.cartogrammar.com/blog/actionscript-curves-update/)

Here is the the drawing code for the approximation method:

// move to the first point
   ctx.moveTo(points[0].x, points[0].y);


   for (i = 1; i < points.length - 2; i ++)
   {
      var xc = (points[i].x + points[i + 1].x) / 2;
      var yc = (points[i].y + points[i + 1].y) / 2;
      ctx.quadraticCurveTo(points[i].x, points[i].y, xc, yc);
   }
 // curve through the last two points
 ctx.quadraticCurveTo(points[i].x, points[i].y, points[i+1].x,points[i+1].y);