Working out a Math Problem

Jan 25 2013 Published by under JavaScript

Send to Kindle

So I had this idea the other day for a graphic effect. I thought it might be interesting to walk through how it went from concept to functional code. So here it is. The effect involved placing smaller circles around a larger circle. Here’s about what I was looking to do:

circle

Note that all the outer circles exactly touch the inner circle and all the outer circles exactly touch each other. There are no spaces and no overlapping. When I went about coding it and found there were a few different ways to do such a thing and it wasn’t as straightforward as I thought it’d be.

I first tried specifying the size of the inner circle and the size of the outer circles. You can then go around the perimeter of the inner circle placing as many outer circles as will fit. Rarely will you get an exact fit this way, which means that if you don’t overlap, you’ll have some space. You can either have one big space at the end, or try to precalculate how many circles will fit and space them out evenly. Not what I wanted.

Another option is to specify the size and number of the outer circles. You can then arrange them so they just touch each other, figure out how far from the center they are, which then lets you calculate the size of the inner circle. This was fairly easy, but not my use case. I wanted to draw circles around an existing circle of known size.

The final option was to specify the size of the inner circle and how many outer circles you want. You then just have to figure out how big to make those outer circles so they sit on the edge of the inner circle and just touch each other. This would work, but I didn’t know how to figure out how big to make the outer circles. Time for some math! And some sketchy sketches.

math01

 

I made the above sketches to get a clear idea of what I wanted to do. On the bottom left is the inner circle with a couple of the outer circles shown. I know the angle between them, as it’s just 2 * PI / number of circles. And I know the radius of the inner circle. All I need is the radius of the outer circles.

The drawing on the top right is a close up of the top triangle in the first one. Angle A is one half of the angle between each circle, or PI / number of circles. R is the radius of the inner circle and N is the radius of the outer circle. From this, I could come up with the equation shown:

sin A * (R + N) = N

In other words, the sine of angle, times the radius (R + N) equals the opposite side (N). Basic trig.

Now I just needed to solve for N. My algebra was a bit rusty, but after a few false starts I got it down:

math02

 

Bottom line: N = (sin A * R) / (1 – sin A)

So I put it down in code and amazingly, it worked. Here’s what I came up with. Be it known that this code has been worked over quite a bit. It was originally just the onload function with one long linear mess of statements and vars. Once I saw that it worked, I refactored it into some halfway decent functions and cleaned it up a bit. It could still use work to make it more reusable, but at least it’s readable enough to post now:

window.onload = function() {
  var canvas = document.getElementById("canvas"),
      context = canvas.getContext("2d"),
      width = canvas.width,
      height = canvas.height,
      circles,
      inner,
      count = 20;
  
  // create inner circle and draw it:
  inner = {
    x: width / 2,
    y: height / 2,
    radius: 100
  };
  drawCircle(inner);
  
  // get outer circles and draw them:
  circles = getOuterCircles(inner, count);
  for(var i = 0; i < circles.length; i += 1) {
    drawCircle(circles[i]);
  }
    
  
  
  function getOuterCircles(inner, count) {
    var circles = [],
        angle = Math.PI * 2 / count, // angle between circles
        outerRadius = getOuterRadius(count, inner.radius),
        r = inner.radius + outerRadius,
        currentAngle;
  
    for(var i = 0; i < count; i += 1) {
      currentAngle = i * angle;
      circles.push({
        x: inner.x + Math.cos(currentAngle) * r,
        y: inner.y + Math.sin(currentAngle) * r,
        radius: outerRadius
      });
    }
    return circles;
  }
  
  function getOuterRadius(count, innerRadius) {
    var s = Math.sin(Math.PI / count);
    return (s * innerRadius) / (1 - s);
  }
  
  function drawCircle(c) {
    context.beginPath();
    context.arc(c.x, c.y, c.radius, 0, Math.PI * 2, false);
    context.stroke();
  }
};

As you can see, a circle is just an object with x, y, and radius properties. Create the inner circle and draw it.

The getOuterCircles function creates an array of circle objects that are placed around the inner one. It needs the inner circle and the count of outer circles to create. This function uses the getOuterRadius function to get the size of the smaller circles. The var s represents sin A in the original equation. And (s * innerRadius) / (1 – s) is basically the (sin A * R) / (1 – sin A) that I originally came up with.

The rest should hopefully be pretty obvious.

To see it in action, check it out here: http://jsbin.com/icahul/1/edit

Send to Kindle

12 responses so far. Comments will be closed after post is one year old.

  • Alex Nino says:

    Hi Keith, I like your trigonometry problems. It is quite interesting when dealing with circles people always look at them from inside out, starting from its pivot and radius. This problem looks to me simpler if you focus only on how many outside circles you have (or want to use), put all of them sitting together and then all you have is vector which is equivalent to the perimeter of the inner circle, and then, simply invert the equation of the circumference which is; 2 * PI * Radius = Perimeter, so you have the radius. Am I missing something here? I hope it helps, cheers!

    • Alex Nino says:

      remember, once you find the radius of the inner circle, just subtract the outer circle radius from it… so here you have your BEARING! beer time! cheers.

    • keith says:

      You day “focus on how many outside circles you have (or want to use), put all of them sitting together…”, but how can you do this if you don’t know what size those outer circles are?

      I started out with several overly simple solutions before realizing that the solution was trickier than I thought. Of course, I’d be delighted if you came up with a simpler solution that I missed. I suggest you fork the jsbin I linked to and try to code your solution. Thanks!

      • Alex Nino says:

        Hi Keith, yeap. the way I look at it, is the other way round, for me it’s easier starting from the outer circles in other to define the perimeter of the main inner circle. In your case, starting from the inner circle first, you have already defined the perimeter of it, it is your constant inner.radius = 100. which dictates everything before hand. the radius of each of the 20 outer circles is equal to: (100*PI*2)/20/2 = 15.7079632… I hope it helps. I am happy to code it when I am back home, cheers.

  • Alex Nino says:

    Hi Keith, here is the simpler solution using inner circle perimeter, your site is rejecting my link: jsbin icahul 50 edit

    • keith says:

      I got the link. There are a couple of flaws in your logic. It looks fine when you first view it, but reduce the count to something like 6 and you’ll see the outer radius is actually too small.

    • keith says:

      I appreciate the back and forth on it though! Keep it coming.

  • Evan Mullins says:

    Nice effect. I’m interested to see it animated! I love how math creeps into design like this.

  • Evan Mullins says:

    jsbin icahul 132 edit
    jerky, but it holds. Thanks for letting me play.

  • [...] you were wondering where my last post was [...]

  • [...] a predetermined size. Sounds like a perfect bonus question from trig, right? Here’s his post: Working out a Math Problem | BIT-101. And then his follow-up post where he gets to what I was expecting in some nice renderings showing [...]