BIT-101 [2003-2017]

Working out a Math Problem


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:

[php lang=“JavaScript”]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();
}
};[/php]

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

« Previous Post
Next Post »