Cubic Bezier Curves – Under the Hood from Peter Nowell on Vimeo.
You can drag the markers and open this example in new tab.
The core part of this practise clearly is the bezier curve. To get all points on the curve for 4 control points this would suffice.
| /** * Computes deCasteljau p(t) for 4 control points. * * @param t - portion on the graph, value between 0.0 and 1.0 * @param cP - 4 control points [x,y] * @return point[x,y] - p(t) */ this.deCasteljau = function (t, cP) { return (1 - t) * (1 - t) * (1 - t) * cP[0][1] + 3 * (1 - t) * (1 - t) * t * cP[1][1] + 3 * (1 - t) * t * t * cP[2][1] + t * t * t * cP[3][1]; }; |
However in order to use an arbitary amount of control points I use the following function. It’s recursively, so potentially it’ll explode the max available stack depth (no clue how much that is in js). But for now it’ll do the job.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /** * Computes deCasteljau p(t) for n-control points * * @param cp - list of control points [][x,y] * @param r - amount points (init value : points.length-1) * @param i - start value (init value : 0) * @param t - portion on the graph (range between 0.0 and 1.0) * * @see http://stackoverflow.com/a/6271870 for n-control points * * @return point[x,y] - point on graph at p(t) */ this.deCasteljau = function (cp, r, i, t) { if (r == 0) return cp[i]; var p1 = this.deCasteljau(cp,r - 1, i, t); var p2 = this.deCasteljau(cp,r - 1, i + 1, t); return [(1 - t) * p1[0] + t * p2[0], (1 - t) * p1[1] + t * p2[1]]; }; |
To use the function simply wrap a loop around with your favorite segments amount.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /** * Compute Points with deCasteljau. * * @param minT - min t (init 0) * @param maxT - max t (init 1) * @param segments - amount segments, the more the smoother (init 20) * @param controlPoints - list of control points [n][x,y] * @return points[segments][x,y] points on bezier curve */ var computePoints = function(minT, maxT, segments, controlPoints) { var points = [segments]; var t; // compute segments + 1 points for (var i = 0; i <= segments; ++i) { // compute t for t_min and t_max t = min_t + i / segments * (min_t + max_t); points[i] = this.deCasteljau(controlPoints,controlPoints.length - 1, 0, t); } return points; }; |
Now simply draw the points.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /** * Draw points. * * @param context * @param points - list of points */ var draw = function (context, points) { context.beginPath(); // check all segment lines for (var i = 1; i <= this.parameter.segments; ++i) { // draw curve [x,y] context.moveTo(points[i - 1][0], points[i - 1][1]); context.lineTo(points[i][0], points[i][1]); } context.closePath(); // set drawing style context.lineWidth = 2; context.strokeStyle = "#ff0000"; // line color // actually start drawing context.stroke(); } |
Et voilà! The animation idea is inspired by Johan Borgström‘s article about bezier curves.
This is my solution to cg2 exercersise a01.2-1.3.
See Also Practical Guide to Bezier Curves