Understanding Quadratic Bézier Curves
In a client project I had to draw by hand a quadratic Bézier curve, because the html5 canvas quadraticCurveTo does not return the information about the path drawn and I needed it.
Some basic explanation of a quadratic Bézier curve. You have a
start point P1
, an end point P2
and a control point C
. Both the x
and
y
of each point on the path is dependant on t
, which will vary from
0 to 1. t=0
at the begining of the curve and t=1
and the end of the curve.
You draw an imaginary line between P1
and C
(P1C
line) and
another one between C
and P2
(CP2
line). For each value of t
, you mark
an imaginary point on P1C
and CP2
. The point on P1C
is at t
of the line,
starting at P1
, and the point on the CP2
line is at t
of the line,
starting at C
. Let's call those points C1
and C2
.
Then, you draw an imaginary line between C1
and C2
, and you mark a real
point at t
of the C1C2
line.
You repeat the procedure for every value of t
, where t
varies from 0 to 1.
All the points you marked on the line C1C2
are showing the quadratic Bézier
curve.
Here's a little animation I made to explain it better:
t = 0
You can have a look at the source for a complete understanding, but here's the important part, edited for clarity:
// constants
var CANVAS_WIDTH = 301;
var CANVAS_HEIGHT = 301;
var p1x = 20;
var p1y = 200;
var cx = 140;
var cy = 20;
var p2x = 280;
var p2y = 280;
// basic setup
var $t = $('#bezier-example-1-t span');
var animationCanvas = $('#bezier-example-1 .animation').get(0);
var animationContext = animationCanvas.getContext('2d');
animationCanvas.width = CANVAS_WIDTH;
animationCanvas.height = CANVAS_HEIGHT;
var curveCanvas = $('#bezier-example-1 .curve').get(0);
var curveContext = curveCanvas.getContext('2d');
curveCanvas.width = CANVAS_WIDTH;
curveCanvas.height = CANVAS_HEIGHT;
curveContext.strokeStyle = "#777";
curveContext.lineWidth = 2;
curveContext.beginPath();
curveContext.moveTo(p1x, p1y);
curveContext.stroke();
setInterval(updateDemo, 1000/30);
var t = 0;
var d = 1; // direction
function updateDemo() { // called 30 times/second to animate
if (t > 1 || t < 0) {
d *= -1; // change direction
curveContext.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
curveContext.beginPath();
}
t += 0.01 * d; // continue moving
$t.html(Math.round(t*100)/100);
// update values
var c1x = p1x + (cx - p1x) * t;
var c1y = p1y + (cy - p1y) * t;
var c2x = cx + (p2x - cx) * t;
var c2y = cy + (p2y - cy) * t;
var tx = c1x + (c2x - c1x) * t;
var ty = c1y + (c2y - c1y) * t;
animationContext.save();
// clear old sketch
animationContext.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
// draw new line
animationContext.beginPath();
animationContext.strokeStyle = '#aaa';
animationContext.lineWidth = 1;
animationContext.moveTo(c1x, c1y);
animationContext.lineTo(c2x, c2y);
animationContext.stroke();
// draw points on lines
drawPoint(animationContext, c1x, c1y, 2, '#0f0');
drawPoint(animationContext, c2x, c2y, 2, '#0f0');
// draw point on curve
drawPoint(animationContext, tx, ty, 3, '#f0f');
animationContext.restore();
// draw the new Bezier curve segment
curveContext.lineTo(tx, ty);
curveContext.stroke();
}
The code is on Github.