d3.layout.orbit = function() { var currentTickStep = 0; var orbitNodes; var orbitSize = [1,1]; var nestedNodes; var flattenedNodes = []; var tickRadianStep = 0.004363323129985824; var orbitDispatch = d3.dispatch('tick'); var tickInterval; var orbitalRings = []; var orbitDepthAdjust = 2.95; function _orbitLayout(_data) { return _orbitLayout; } _orbitLayout.mode = function() { //Atomic, Solar, other? } _orbitLayout.start = function() { //activate animation here tickInterval = setInterval( function() { currentTickStep++; flattenedNodes.forEach(function(_node){ if (_node.parent) { _node.x = _node.parent.x + ( (_node.parent.ring / 2) * Math.sin( _node.angle + (currentTickStep * tickRadianStep)) ); _node.y = _node.parent.y + ( (_node.parent.ring / 2) * Math.cos( _node.angle + (currentTickStep * tickRadianStep)) ); } }) orbitalRings.forEach(function(_ring) { _ring.x = _ring.source.x; _ring.y = _ring.source.y; }) orbitDispatch.tick(); }, 10); } _orbitLayout.stop = function() { //deactivate animation here clearInterval(tickInterval); } _orbitLayout.speed = function(_degrees) { if (!arguments.length) return tickRadianStep / (Math.PI / 360); tickRadianStep = tickRadianStep = _degrees * (Math.PI / 360); return this; } _orbitLayout.size = function(_value) { if (!arguments.length) return orbitSize; orbitSize = _value; return this; //change size here } _orbitLayout.orbitSize = function(_value) { //change ring size reduction (make that into dynamic function) if (!arguments.length) return orbitDepthAdjust; orbitDepthAdjust = _value; return this } _orbitLayout.orbitalRings = function() { //return an array of data corresponding to orbital rings if (!arguments.length) return orbitalRings; return this; } _orbitLayout.nodes = function(_data) { if (!arguments.length) return flattenedNodes; nestedNodes = _data; calculateNodes(); return this; } d3.rebind(_orbitLayout, orbitDispatch, "on"); return _orbitLayout; function calculateNodes() { var _data = nestedNodes; //If you have an array of elements, then create a root node (center) //In the future, maybe make a binary star kind of thing? if (!_data.values) { orbitNodes = {key: "root", values: _data} orbitNodes.values.forEach(function (_node) { _node.parent = orbitNodes; }) } //otherwise assume it is an object with a root node else { orbitNodes = _data; } orbitNodes.x = orbitSize[0] / 2; orbitNodes.y = orbitSize[1] / 2; orbitNodes.deltaX = function(_x) {return _x} orbitNodes.deltaY = function(_y) {return _y} orbitNodes.ring = orbitSize[0] / 2; orbitNodes.depth = 0; flattenedNodes.push(orbitNodes); traverseNestedData(orbitNodes) function traverseNestedData(_node) { if(_node.values) { var thisPie = d3.layout.pie().value(function() {return 1}); var piedValues = thisPie(_node.values); orbitalRings.push({source: _node, x: _node.x, y: _node.y, r: _node.ring / 2}); for (var x = 0; x<_node.values.length;x++) { _node.values[x].angle = piedValues[x].endAngle; _node.values[x].parent = _node; _node.values[x].depth = _node.depth + 1; _node.values[x].x = _node.values[x].parent.x + ( (_node.values[x].parent.ring / 2) * Math.sin( _node.values[x].angle ) ); _node.values[x].y = _node.values[x].parent.y + ( (_node.values[x].parent.ring / 2) * Math.cos( _node.values[x].angle ) ); _node.values[x].deltaX = function(_x) {return _x} _node.values[x].deltaY = function(_y) {return _y} _node.values[x].ring = _node.values[x].parent.ring / orbitDepthAdjust; flattenedNodes.push(_node.values[x]); traverseNestedData(_node.values[x]); } } } } }