Arrange circles of different sizes around a circular path without spaces
I am creating a Canvas animation and have managed to set x the number of circles in a circular path. Here's a simplified version of the code I'm using:
var total = circles.length,
i = 0,
radius = 500,
angle = 0,
step = (2*Math.PI) / total;
for( i; i < total; i++ ) {
var circle = circles[i].children[0],
cRadius = circle[i].r,
x = Math.round(winWidth/2 + radius * Math.cos( angle ) - cRadius),
y = Math.round(winHeight/2 + radius * Math.sin( angle ) - cRadius);
circle.x = x;
circle.y = y;
angle += step;
}
The result is the following:
I'm trying to get all the circles next to each other with no spaces in between (except the first and last). The dimensions of the circles (radius) are randomly generated and should not be adjusted to match the circular path:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
I expect there will be a gap between the first and last lap.
I am struggling to get around this, so any help would be greatly appreciated :)
Hooray!
source to share
It's something like this, but you need to figure out the size of the last circle:
http://jsfiddle.net/rudiedirkx/ufvf62yf/2/
Basic logic:
var firstStep = 0, rad = 0, step = 0;
firstStep = step = stepSize();
for ( var i=0; i<30; i++ ) {
draw.radCircle(rad, step);
rad += step;
step = stepSize();
rad += step;
}
-
stepSize()
creates a random delight betweenMath.PI/48
andMath.PI/48 + Math.PI/36
(no particular reason, but it looked good). You can fix this as correct dimensions. -
draw.radCircle(rad, step)
creates a circle at arad
dimension positionstep
(also in a rad). -
step
is added twice per iteration: once to go from the current center of the circle to its edge, and once to find the next center of the circle -
firstStep
important because you have to know where to stop (since the first circle crosses the negative angle) - I haven't figured out how to make the last circle the perfect size yet =)
There's also a bit of magic in draw.radCircle()
:
var r = rRad * Math.PI/3 * 200 * .95;
-
200
- this is obviously a large circle radius -
95%
is that the length of the edge of the circle is slightly longer (straight) than the radius of each circle - I have no idea why
Math.PI/3
this is what ... I figured it should be Math.PI / 2, which is 1 rad, but that didn't work at all.1/3
for some reason ..... Explain what!
If you want to animate these circle dimensions and keep them aligned, you will find it difficult. Now they are all random. In animation, only the first iteration can be random and the rest is a big mess in cache and math =)
source to share
The main creation loop:
• take the current radius
• calculate the corners that it covers (= atan2 (smallRadius / bigRadius))
• move the base corner to this last corner.
http://jsfiddle.net/dj2v7mbw/9/
function CircledCircle(x, y, radius, margin, subCircleCount, subRadiusMin, subRadiusMax) {
this.x = x;
this.y = y;
this.radius = radius;
this.subCircleCount = subCircleCount;
var circles = this.circles = [];
// build top sub-circles
var halfCount = Math.floor(subCircleCount / 2);
var totalAngle = addCircles(halfCount);
// re-center top circles
var correction = totalAngle / 2 + Math.PI / 2;
for (var i = 0; i < halfCount; i++) this.circles[i].angle -= correction;
// build bottom sub-circles
totalAngle = addCircles(subCircleCount - halfCount);
// re-center bottom circles
var correction = totalAngle / 2 - Math.PI / 2;
for (var i = halfCount; i < subCircleCount; i++) this.circles[i].angle -= correction;
// -- draw this C
this.draw = function (angleShift) {
for (var i = 0; i < this.circles.length; i++) drawDistantCircle(this.circles[i], angleShift);
}
//
function drawDistantCircle(c, angleShift) {
angleShift = angleShift || 0;
var thisX = x + radius * Math.cos(c.angle + angleShift);
var thisY = y + radius * Math.sin(c.angle + angleShift);
ctx.beginPath();
ctx.arc(thisX, thisY, c.r, 0, 2 * Math.PI);
ctx.fillStyle = 'hsl(' + (c.index * 15) + ',75%, 75%)';
ctx.fill();
ctx.stroke();
}
//
function addCircles(cnt) {
var currAngle = 0;
for (var i = 0; i < cnt; i++) {
var thisRadius = getRandomInt(subRadiusMin, subRadiusMax);
var thisAngle = Math.atan2(2 * thisRadius + margin, radius);
var thisCircle = new subCircle(thisRadius, currAngle + thisAngle / 2, i);
currAngle += thisAngle;
circles.push(thisCircle);
}
return currAngle;
}
}
from
function subCircle(radius, angle, index) {
this.r = radius;
this.angle = angle;
this.index = index;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
use
var myCircles = new CircledCircle(winWidth / 2, winHeight / 2, 350, 2, 24, 5, 50);
myCircles.draw();
animate with:
var angleShift = 0;
function draw() {
requestAnimationFrame(draw);
ctx.clearRect(0, 0, winWidth, winHeight);
myCircles.draw(angleShift);
angleShift += 0.010;
}
draw();
source to share