Skip to content Skip to sidebar Skip to footer

D3.js User Viewer Chart

Developing a chart which will provide a user a visual for who has viewed them. The inner circle shows matches (user following/being followed), the next ring - user following others

Solution 1:

"LATEST CODE" - http://jsfiddle.net/NYEaX/336/ (Note: Merely conceptual, highly non-optimized!)

I'm not sure if d3 can do this automatically somehow, but you can always just figure out the x and y positions of your dots yourself with some math:

One way to do it is to just place each dot randomly until it sits correctly. This is definitely not the fastest approach, but if you don't have too many dots to place, it might still be way good enough.

Let's say your smallest circle has radius rs and center point (xs, ys) and your middle circle rm and (xm, ym) and your largest circle rl and (xl, yl), then you can check which circle a dot does or does not fall into by comparing its distance from the center-point of the circle to the radius of that circle:

For example, if you want to place a dot into the middle circle:

  • Pick a random position (x, y) so that it can fall anywhere in that circle:
    • xm - rmxxm + rm
    • ym - rmyym + rm
  • Make sure it actually is inside the circle:
    • Check (x - xm) + (y - ym) ≤ rm
    • If this is not the case, start over and pick a different (x, y)
  • Make sure it is not inside the smaller circle:
    • Check (x - xs) + (y - ys) > rs
    • If this is not the case, start over and pick a different (x, y)

Now you have some random point that is inside the middle circle, but not the small one. Placing one into the larger circle works the same way (just replace xm, ym, and rm with xl, yl, and rl as well as xs, ys, and rs with xm, ym, and rm). To place a dot into the smallest circle, you only need to check the first condition (with xm, ym, and rm replaced by xs, ys, and rs).

Now, you can either stop here (but depending on your dot-density, this might not be the prettiest picture) or, you can try to space the dots a little nicer relative to each other. One way to do this is the following:

  • Loop over all dots (x1, y1) ... (xn, yn)
  • Calculate the distance from each dot to each other dot
  • Find the two that are closest together
  • Of those two, figure out which one is closer to its neighbors
  • Remove this dot and place it again randomly using the method described above
  • Either repeat this some number of times or until you get a minimum distance between any two dots that is acceptable (I would just try out what works best)

By adjusting the two check-equations above, you can also add a little bit of clearance around the borders of your circles that the dots will avoid, by replacing rm with (rm - d) and rs with (rs + d).

There are definitely a bunch of ways to optimize this simple approach: For example you can pick your initial (x, y) position in polar coordinates by choosing r and α instead and then calculating x and y from them to make sure that you never pick a dot that is not inside the circle you want. Just make sure you pick r from a correctly weighted distribution so that x and y turn out to be uniformly distributed. Also, you can check the distance from a newly placed dot to all its neighbors immediately when placing the dot rather than in a second step at the end. For example, find 10 places for each dot and then always pick the one out of those with the largest distance to its closest neighbor.

UPDATE

And this is what it looks like without worrying about dot spacing: http://jsfiddle.net/NYEaX/328/

The relevant section implementing the dot placement is here (variable names changed to match above):

function getXCoordinateInCircleSection(i, xm, ym, rm, xs, ys, rs) {
    done = false;
    while (!done) {
        x = getRandomCoordinate(xm-rm, xm+rm);
        y = getRandomCoordinate(ym-rm, ym+rm);
        done = true;
        if ((x-xm)*(x-xm)+(y-ym)*(y-ym) >  (rm-10)*(rm-10)) done = false;
        if ((x-xs)*(x-xs)+(y-ys)*(y-ys) <= (rs+10)*(rs+10)) done = false;
    }
    window.ys[i] = y;
    return x;
}

function getYCoordinateInCircleSection(i) {
    return window.ys[i];
}

And with improved dot spacing: http://jsfiddle.net/NYEaX/329/

(Both of these are based on http://jsfiddle.net/NYEaX/326/ created by @TheOldCounty)

Post a Comment for "D3.js User Viewer Chart"