|
// based off of http://labs.unwieldy.net/moocirclepack/ |
|
org.polymaps.packer = function() { |
|
var packer = {}, |
|
nodes = [], |
|
elements = [], |
|
timesToPack = 50; |
|
|
|
packer.elements = function(e) { |
|
if (!arguments.length) { |
|
return elements; |
|
} |
|
|
|
elements = e; |
|
|
|
return packer; |
|
}; |
|
|
|
packer.start = function() { |
|
populateNodes(); |
|
|
|
var j = 0; |
|
var intervalId = setInterval(function() { |
|
// limit the number of times it packs |
|
if (j++ > timesToPack) { |
|
clearInterval(intervalId); |
|
intervalId = null; |
|
} |
|
|
|
draw(); |
|
}, 40); |
|
|
|
return packer; |
|
}; |
|
|
|
var populateNodes = function() { |
|
nodes = []; |
|
|
|
for (var i = 0; i < elements.length; i++) |
|
{ |
|
// thanks to zain + mbostock for making me less afraid of regexes |
|
var at = elements[i].getAttribute("transform"), |
|
av = at.match(/translate\((.*),(.*)\)/); |
|
ax = Number(av[1]), |
|
ay = Number(av[2]), |
|
ar = elements[i].getAttribute("r"); |
|
|
|
var n = { |
|
x: ax, |
|
y: ay, |
|
r: parseFloat(ar), |
|
id: i |
|
}; |
|
|
|
nodes[i] = n; |
|
} |
|
}; |
|
|
|
var draw = function() { |
|
for (var i = 0; i < elements.length; i++) { |
|
for (var j = 0; j < elements.length; j++) { |
|
if (i == j) { |
|
continue; |
|
} |
|
|
|
pack(nodes[i], nodes[j]); |
|
} |
|
|
|
var e = elements[i]; |
|
var n = nodes[i]; |
|
e.setAttribute("transform", "translate(" + n.x + "," + n.y + ")"); |
|
} |
|
}; |
|
|
|
// circle pack |
|
// if the radii intersect, push them apart |
|
var pack = function(a, b) { |
|
if (intersects(a, b)) { |
|
v.x = a.x - b.x; |
|
v.y = a.y - b.y; |
|
var d = (v.x * v.x) + (v.y * v.y); |
|
|
|
v.normalize(); |
|
v.mult((a.r + b.r + 1 - Math.sqrt(d)) * 0.25); |
|
|
|
a.x += v.x; |
|
a.y += v.y; |
|
|
|
b.x -= v.x; |
|
b.x -= v.y; |
|
} |
|
}; |
|
|
|
// vector |
|
var v = {}; |
|
v.normalize = function() { |
|
v.magnitude = Math.sqrt((v.x * v.x) + (v.y * v.y)); |
|
|
|
v.x = v.x / v.magnitude; |
|
v.y = v.y / v.magnitude; |
|
}; |
|
|
|
v.mult = function(m) { |
|
v.x *= m; |
|
v.y *= m; |
|
}; |
|
|
|
var distance = function(ax, ay, bx, by) { |
|
var dx = ax - bx; |
|
var dy = ay - by; |
|
|
|
return Math.sqrt((dx*dx) + (dy*dy)); |
|
}; |
|
|
|
var intersects = function(a, b) { |
|
var d = distance(a.x, a.y, b.x, b.y); |
|
return (d < (a.r + b.r + 1)); |
|
}; |
|
|
|
return packer; |
|
}; |