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?

What do you mean by "smooth"? Infinitely differentiable? Twice differentiable? Cubic splines ("Bezier curves") have many good properties and are twice differentiable, and easy enough to compute.
– Kerrek SBAug 14 '11 at 1:15

@sketchfemme, are you rendering the lines in real-time, or delaying the rendering until after collecting a bunch of points?
– CrashalotMar 27 '12 at 21:21

@Crashalot I am collecting the points into an array. You need at least 4 points to use this algorithm. After that you can render in real time on a canvas by clearing the screen on each call of mouseMove
– HomanApr 23 '12 at 18:40

9 Answers
9

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/)

First off: This is gorgeous. :-) But looking at that image, doesn't it give the (misleading) impression that the values actually went below value #10 en route between #9 and #10? (I'm counting from actual dots I can see, so #1 would be the one near the top of the initial downward trajectory, #2 the one at the very bottom [lowest point in the graph], and so on...)
– T.J. CrowderSep 27 '13 at 16:13

5

Just want to say that after days of searching, this was the only util that actually worked exactly as I wanted. Thanks so much
– cnpFeb 10 '14 at 3:27

To add to K3N's cardinal splines method and perhaps address T. J. Crowder's concerns about curves 'dipping' in misleading places, I inserted the following code in the getCurvePoints() function, just before res.push(x);

This effectively creates a (invisible) bounding box between each pair of successive points and ensures the curve stays within this bounding box - ie. if a point on the curve is above/below/left/right of both points, it alters its position to be within the box. Here the midpoint is used, but this could be improved upon, perhaps using linear interpolation.

Incredibly late but inspired by Homan's brilliantly simple answer, allow me to post a more general solution (general in the sense that Homan's solution crashes on arrays of points with less than 3 vertices):