Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I draw a circle on a canvas?

I'm using javascript and a canvas to draw a mathematically designed scale (for measuring torque, it includes newton-meters and foot-pounds). I had been positioning my ticks using trigonometry, and drawing arcing lines using arc, naturally. The problem came when they needed to line up, but there was some strange distortion. I then drew a very large circle and compared it with a circular selection in GIMP and there was a distortion. The circles are consistent every 45 degrees around the circles, but between those nodes the canvas drawn circle deviates outwards, much like an octagon that can been rounded too far outwards to approximate a circle.

Why do these distortions occur? How can I draw a truly circular circle on a canvas?

This is the code I used to generate my distorted circle.

<!DOCTYPE html>
<html>
  <body>
    <canvas width=8000 height=8000 id='myCanvas'></canvas>
    <script>
      var canvas = document.getElementById('myCanvas');
      var context = canvas.getContext('2d');
      context.beginPath();
      context.arc(4000, 4000, 3200, 0, Math.PI*2, false);
      context.lineWidth = 1;
      context.strokeStyle = '#ff0000';
      context.lineCap = 'square';
      context.stroke();
    </script>
  </body>
</html>

This may not be minimal, I know not of the relevance of context.lineCap, but it's a vestige of the code this is based on. The following screenshot shows the difference between the circle drawn by GIMP and the circle drawn by Chrome screenshot

like image 707
Robbie Mckennie Avatar asked Jul 14 '15 08:07

Robbie Mckennie


Video Answer


1 Answers

This is Chromium Bug #164241 (declared fixed, although the fix may not have made it out yet). The short answer is: Chrome is approximating circles with composite Bezier curves.

I was unable to replicate this myself on Chromium (43.0.2357.130, Ubuntu) but it does occur on Firefox 39, although I couldn't find a similar bug report for Firefox. The fact that it's correct every 90 degrees (not 45, at least not for me) indicates that circles are being approximated by 4 curves, as the curve endpoints are guaranteed to be correct.

Luckily there's a simple workaround: create a path consisting of more than 4 curves. 8 or even 6 should be sufficient for the radii you're using but you can use more if you want to play it safe. Or you could increase the number of curves as a function of the radius, although calculating the optimal function (the minimum n that will produce an accurate circle for a given radius r) is non-trivial.

context.beginPath();
var n=8; // 4 is the default behaviour. higher is better, also slower
for (var i=0; i<n; i++) {
    context.arc(4000, 4000, 3200, Math.PI*2*i/n, Math.PI*2*(i+1)/n, false);
}
context.stroke();

See also this question.

(And yes, lineCap is a red herring.)

like image 135
hexwab Avatar answered Oct 30 '22 09:10

hexwab