Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to place svg shapes in a circle?

Tags:

d3.js

I'm playing a bit with D3.js and I got most things working. But I want to place my svg shapes in a circle. So I will show the difference in data with color and text. I know how to draw circles and pie charts, but I want to basically have a circle of same size circles. And not have them overlap, the order is irrelevant. I don't know where to start, to find out the x & y for each circle.

like image 422
user1870877 Avatar asked Dec 02 '12 19:12

user1870877


2 Answers

If I understand you correctly, this is a fairly standard math question:

Simply loop over some angle variable in the appropriate step size and use sin() and cos() to calculate your x and y values.

For example:

Let's say you are trying to place 3 objects. There are 360 degrees in a circle. So each object is 120 degrees away from the next. If your objects are 20x20 pixels in size, place them at the following locations:

 x1 = sin(  0 * pi()/180) * r + xc - 10;  y1 = cos(  0 * pi()/180) * r + yc - 10
 x2 = sin(120 * pi()/180) * r + xc - 10;  y2 = cos(120 * pi()/180) * r + yc - 10
 x3 = sin(240 * pi()/180) * r + xc - 10;  y3 = cos(240 * pi()/180) * r + yc - 10

Here, r is the radius of the circle and (xc, yc) are the coordinates of the circle's center point. The -10's make sure that the objects have their center (rather than their top left corner) on the circle. The * pi()/180 converts the degrees to radians, which is the unit most implementations of sin() and cos() require.

Note: This places the shapes equally distributed around the circle. To make sure they don't overlap, you have to pick your r big enough. If the objects have simple and identical boundaries, just lay out 10 of them and figure out the radius you need and then, if you need to place 20, make the radius twice as big, for 30 three times as big and so forth. If the objects are irregularly shaped and you want to place them in the optimal order around the circle to find the smallest circle possible, this problem will get extremely messy. Maybe there's a library for this, but I don't have one in the top of my head and since I haven't used D3.js, I'm not sure whether it will provide you with this functionality either.

like image 110
Markus A. Avatar answered Oct 08 '22 03:10

Markus A.


Here's another approach to this, for shapes of arbitrary size, using D3's tree layout: http://jsfiddle.net/nrabinowitz/5CfGG/

The tree layout (docs, example) will figure out the x,y placement of each item for you, based on a given radius and a function returning the separation between the centers of any two items. In this example, I used circles of varying sizes, so the separation between them is a function of their radii:

var tree = d3.layout.tree()
    .size([360, radius])
    .separation(function(a, b) {
        return radiusScale(a.size) + radiusScale(b.size);
    });

Using the D3 tree layout solves the first problem, laying out the items in a circle. The second problem, as @Markus notes, is how to calculate the right radius for the circle. I've taken a slightly rough approach here, for the sake of expediency: I estimate the circumference of the circle as the sum of the diameters of the various items, with a given padding in between, then calculate radius from the circumference:

var roughCircumference = d3.sum(data.map(radiusScale)) * 2 +
        padding * (data.length - 1),
    radius = roughCircumference / (Math.PI * 2);

The circumference here isn't exact, and this will be less and less accurate the fewer items you have in the circle, but it's close enough for this purpose.

like image 31
nrabinowitz Avatar answered Oct 08 '22 05:10

nrabinowitz