I'm developing an application where users draw euclidean constructions on the HTML5 canvas. As such I can't really limit the size of certain shapes. When exaploring very large circles being drawn on the screen I noticed that very large circles don't have a constant radius.
To be more specific, a circle defined by two points, a center point and one specifing the radius doesn't pass throught the radius point anymore!
Progressivly larger circles. These are all supposed to pass through point E.
The error doesn't occure on multiples of 45 degrees = PI/4. Between these multiples the error is biggest (PI/8 for example)
Here is a jsfiddle containing the first example above:
http://jsfiddle.net/D28J2/2/
My questions: Why does this occure? and Is there some way to (efficently) work around this?
The way I worked around this issue completely was roll my own implementation of a circle draw approximation with bezier curves. An article detailing the implementation can be found here http://www.tinaja.com/glib/ellipse4.pdf.
function magic_circle(ctx, x, y, r){
m = 0.551784
ctx.save()
ctx.translate(x, y)
ctx.scale(r, r)
ctx.beginPath()
ctx.moveTo(1, 0)
ctx.bezierCurveTo(1, -m, m, -1, 0, -1)
ctx.bezierCurveTo(-m, -1, -1, -m, -1, 0)
ctx.bezierCurveTo(-1, m, -m, 1, 0, 1)
ctx.bezierCurveTo( m, 1, 1, m, 1, 0)
ctx.closePath()
ctx.restore()
}
With just these four segements I was able to approximate a circle much better then the build in google chrome canvas implementation.
This is probably a floating point cutoff error. Possibly because sine and cosine aren't giving perfectly accurate values. You can get around it (in Chrome at least) by rotating the canvas instead of the arc.
ctx.save(); // Save the canvas so we can rotate back.
ctx.translate(x, y); // Translate to the origin point.
ctx.rotate(alpha); // Rotate the proper angle.
ctx.arc(0, 0, 3, 0, Math.PI*2); // Draw the small circle at the origin.
ctx.fill();
ctx.arc(r, 0, r, 0, Math.PI*2); // Create a big with the origin 1 radius away.
ctx.restore(); // Restore the canvas to the original orientation
// before drawing. Otherwise the circle looks bad.
ctx.strokeStyle = "black";
ctx.stroke(); // Draw!
I am a big fan of manipulating the canvas instead of shapes. It gives you a more logical area to work with. See http://jsfiddle.net/D28J2/10/
Just throwing this out there but could it be an issue without specifying enough digits of PI? Whenever I do things like that I tend to go slightly overboard and use about 10 digits of PI.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With