ellipse() bezier

A JS polyfill for ctx.ellipse()

by dirtyd77

HTML

<canvas id="canvas" width=400 height=400></canvas>

JavaScript

// Compare ctx.ellipse() with a polyfill using cubic bezier curves.
//
// The real ellipse is drawn in thick light red, and the bezier curves
// are drawn in thin black. Clicking and dragging allows to compare the 
// function over different ellipses shapes.
//
// This code is in the public domain (CC0). 

var ctx = canvas.getContext('2d');

// canvas.ellipse function, with a 4 pixel bigger x and y radius
function drawRealEllipse(x, y, rx, ry, angle) {
    ctx.beginPath();
    ctx.ellipse(x, y, rx, ry, angle, 0, 2 * Math.PI);

    ctx.lineWidth = 7;
    ctx.strokeStyle = 'rgba(255, 0, 0, 0.25)';
    ctx.stroke();
}

// approximation using four cubic splines
function drawApproximateEllipse(x, y, rx, ry, angle) {
    var c = 0.551784; // see http://www.tinaja.com/glib/ellipse4.pdf

	/* ctx.translate(x, y) */
	/* ctx.rotate(angle) */

    ctx.beginPath();
    ctx.moveTo(-rx, 0) // start point of first curve
    ctx.bezierCurveTo(-rx,  ry * c, -rx * c,  ry, 0,  ry);
    ctx.bezierCurveTo( rx * c,  ry,  rx,  ry * c,  rx, 0);
    ctx.bezierCurveTo( rx, -ry * c,  rx * c, -ry, 0, -ry);
    ctx.bezierCurveTo(-rx * c, -ry, -rx, -ry * c, -rx, 0);

	/* ctx.rotate(-angle) */
	/* ctx.translate(-x, -y) */

    ctx.lineWidth = 1;
    ctx.strokeStyle = 'rgba(0, 0, 0, 1.0)';
    ctx.stroke();


}

function drawBoth(x, y, rx, ry, angle) {
    drawRealEllipse(x, y, rx, ry, angle);
    drawApproximateEllipse(x, y, rx, ry, angle);
}

drawBoth(200, 200, 50, 100, 30);

// adding interactive testing
var mx1 = 200,
    my1 = 200,
    isDown = false;

canvas.onmousedown = function() {
    isDown = true;
}
canvas.onmouseup = function() {
    isDown = false;
}

canvas.onmousemove = function(e) {
    if (!isDown) return;
    mx2 = e.clientX;
    my2 = e.clientY;

    ctx.clearRect(0, 0, 400, 400);
    drawBoth((mx1 + mx2) / 2, (my1 + my2) / 2,
        Math.abs(mx2 - mx1) / 2, Math.abs(my2 - my1) / 2, 25);
}