If we draw some lines on the sample, you can see that the control point is found by translating P2 up and over by L1 and L2. The question is how do we find L1 and L2? The problem can be broken down into two steps:

1) L1 is the length of the line that is perpendicular to P1,P3 and runs to P2.

2) L2 is the length of the line from the midpoint of P1,P3 to the intersection found in step 1.

The first step requires us to project P2 onto P1,P3 to find the intersection point. Once we have that point, the two length are easy to calculate to perform the translation on P2 to determine the control point. Below is the implementation of this process:

function findControlPoint(s1, s2, s3)

{

var // Unit vector, length of line s1,s3

ux1 = s3.x - s1.x,

uy1 = s3.y - s1.y,

ul1 = Math.sqrt(ux1*ux1 + uy1*uy1)

u1 = { x: ux1/ul1, y: uy1/ul1 },

// Unit vector, length of line s1,s2

ux2 = s2.x - s1.x,

uy2 = s2.y - s1.y,

ul2 = Math.sqrt(ux2*ux2 + uy2*uy2),

u2 = { x: ux2/ul2, y: uy2/ul2 },

// Dot product

k = u1.x*u2.x + u1.y*u2.y,

// Project s2 onto s1,s3

il1 = { x: s1.x+u1.x*k*ul2, y: s1.y+u1.y*k*ul2 },

// Unit vector, length of s2,il1

dx1 = s2.x - il1.x,

dy1 = s2.y - il1.y,

dl1 = Math.sqrt(dx1*dx1 + dy1*dy1),

d1 = { x: dx1/dl1, y: dy1/dl1 },

// Midpoint

mp = { x: (s1.x+s3.x)/2, y: (s1.y+s3.y)/2 },

// Control point on s2,il1

cpm = { x: s2.x+d1.x*dl1, y: s2.y+d1.y*dl1 },

// Translate based on distance from midpoint

tx = il1.x - mp.x,

ty = il1.y - mp.y,

cp = { x: cpm.x+tx, y: cpm.y+ty };

return cp;

}

Here, points are described by objects with a x and y property (ie.

`{ x: 150, y: 210 }`

). To use the function, just pass in the points you want used to draw the curve and use the returned control point in the quadraticCurveTo() function:

function drawCurve($canvas, p1, p2, p3)

{

var ctx = $canvas[0].getContext('2d'),

cp = findControlPoint(p1, p2, p3);

ctx.strokeStyle = 'black';

ctx.strokeWidth = 1;

ctx.beginPath();

ctx.moveTo(p1.x, p1.y);

ctx.quadraticCurveTo(cp.x,cp.y,p3.x,p3.y);

ctx.stroke();

}

I created a demo in my sandbox to allow moving the points around and visually see the resulting control point and curve.

There is a small caveat to keep in mind - the second point won't always be on the extreme point of the curve. You can see this best by moving the point further to the side by the start/end points. I believe this has to do with the function assuming that the second point is at the midpoint of curve (in terms of the parametric function, this is t=0.5). Unfortunately, you have no control over that selection. One way to work around it is to use path interpolation to draw the curve.

The demo draws the same curve using the PathJS library which will put the second point much closer to the extreme of the curve. This might cause some sharper angles in some cases but will put the second point very close to where the curve changes directions. The image on the left uses the same points as the example above but used PathJS to interpolate the path.

With the examples here, you can now draw bezier curves with whatever information you have available. If you already have the control point, then you're all set. If not, the function presented here will provide an accurate point to use as the control point required by the quadraticCurveTo() function. The same basic approach can be extended to find two control points that are required in the cubicCurveTo() function as well.