Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Very large HTML5 canvas circle imprecise

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!

Large circle with radius point

Progressivly larger circles. These are all supposed to pass through point E.

Larger circles

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?

like image 848
Mathijs Henquet Avatar asked Jan 03 '12 15:01

Mathijs Henquet


3 Answers

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.

like image 86
Mathijs Henquet Avatar answered Oct 15 '22 05:10

Mathijs Henquet


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/

like image 22
Brian Nickel Avatar answered Oct 15 '22 05:10

Brian Nickel


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.

like image 1
Sean Tublewicz Avatar answered Oct 15 '22 06:10

Sean Tublewicz