Randomize and animate multiple Start and End Angles of Arc - animation

I'm trying to create a visual that is a modification of the Arc Tween demo. In it, I want an array of data to define the colors and general radius of each arc, and on an interval, both the start and end angles should slowly animate. But I think the way I'm defining each arc is causing things to over animate.
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://d3js.org/d3.v4.min.js"></script>
<title>Arcs</title>
</head>
<body>
<svg width="960" height="500"></svg>
</body>
</html>
SCRIPT
// Modified from Arc Tween
// https://bl.ocks.org/mbostock/5100636
var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto
var overlap = 50
var jsonArcs = [
{ "base_radius": 370, "color" : "red"},
{ "base_radius": 330, "color" : "orange"},
{ "base_radius": 290, "color" : "yellow"},
{ "base_radius": 250, "color" : "green"},
{ "base_radius": 210, "color" : "blue" },
{ "base_radius": 170, "color" : "purple"},
{ "base_radius": 130, "color" : "black"},
{ "base_radius": 90, "color" : "red"}
];
var arc = d3.arc()
.startAngle(function(d) { return Math.random() * tau; })
.endAngle(function(d) { return Math.random() * tau; })
.innerRadius(function(d) { return d.base_radius - overlap * Math.random(); })
.outerRadius(function(d) { return d.base_radius + overlap * Math.random(); });
var center_def = d3.arc()
.innerRadius(0)
.outerRadius(60)
.startAngle(0);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = g.selectAll("path")
.data(jsonArcs)
.enter().append("path")
.attr("fill", function(d, i) { return d.color; })
.attr("d", arc);
var center = g.append("path")
.datum({endAngle: tau})
.style("fill", "black")
.attr("d", center_def);
d3.interval(function() {
path.transition()
.duration(750)
.attrTween("d", arcTween(Math.random() * tau, arc));
}, 2500);
function arcTween(newAngle, obj) {
return function(d) {
var interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
d.endAngle = interpolate(t);
return obj(d);
};
};
}
Instead of each arc smoothly animating from start angles to new angles, the entire visual jumps multiple times to a new state.
How do I configure this function such that each arc smoothly transitions its start and end angles from old to new?

There are a few issues here
Your arc function is going to return random radii every call, which is not what I think you want. You could transition an arc from one inner/outer radius to the next, but for simplicity, let's say that each path only initially gets a random radius
In order to transition from one the old pair of start/end angles to the new one you'll need to store the current angles somewhere. We'll store it in a local variable, which will be tied to each path
Because each path will have a different inner/outer radius, we'll need to have a different arc function for each segment as well.
working code here:
var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto
var overlap = 50;
var currentSegment = d3.local();
var segmentRadius = d3.local();
var jsonArcs = [
{ "base_radius": 370, "color" : "red"},
{ "base_radius": 330, "color" : "orange"},
{ "base_radius": 290, "color" : "yellow"},
{ "base_radius": 250, "color" : "green"},
{ "base_radius": 210, "color" : "blue" },
{ "base_radius": 170, "color" : "purple"},
{ "base_radius": 130, "color" : "black"},
{ "base_radius": 90, "color" : "red"}
];
var arc = d3.arc()
.innerRadius(function() { return segmentRadius.get(this).innerRadius })
.outerRadius(function() { return segmentRadius.get(this).outerRadius });
var center_def = d3.arc()
.innerRadius(0)
.outerRadius(60)
.startAngle(0);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = g.selectAll("path")
.data(jsonArcs)
.enter().append("path")
.attr("fill", function(d, i) { return d.color; })
.each(function(d) {
var angles = randomAngles();
d.startAngle = angles.startAngle;
d.endAngle = angles.endAngle;
segmentRadius.set(this, {
innerRadius: d.base_radius - overlap * Math.random(),
outerRadius: d.base_radius + overlap * Math.random()
});
})
.attr("d", arc)
.each(function(d) {currentSegment.set(this, d)});
var center = g.append("path")
.datum({endAngle: tau})
.style("fill", "black")
.attr("d", center_def);
d3.interval(function() {
path.transition()
.duration(750)
.attrTween("d", arcTween);
}, 2500);
function arcTween() {
var thisPath = this;
var interpolate = d3.interpolate(currentSegment.get(this), randomAngles());
currentSegment.set(this, interpolate(0));
return function(t) {
return arc.call(thisPath, interpolate(t));
};
}
function randomAngles() {
var angles = [Math.random() * tau, Math.random() * tau].sort();
return {startAngle: angles[0], endAngle: angles[1]};
}
Notice a few things about the changed code:
I set the random initial angles in an each call on the path just before the "d" attribute is set
I stored the segment radii in the segmentRadius d3.local variable at the end of that same chain and also after each interpolation is set in the call to the transition
In the transition function I needed to call arc to preserve the 'this' of the path so that 'this' would be correct in arc.innerRadius when retrieving the segmentRadius.
d3.interpolate can happily handle objects and not just numbers.
I have a similar example that I've been working on here if you'd like to learn more.

Related

D3 - flare not showing all data

Here is the code I'm working with. I'm generating data in php and sending that to d3 via json:
php file:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
stroke: #fff;
}
</style>
<body>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"> </script>
<script type="text/javascript" src="flare.js"></script>
<?php
// Move php data to JSON to be used in d3 apps
$flare_child_1 = array("name"=> "subchild1", "size"=> 90);
$flare_child_2 = array("name"=> "subchild2", "size"=> 10);
$flare_child_3 = array("name"=> "subchild3", "size"=> 55);
$flare_child_4 = array("name"=> "subchild4", "size"=> 72);
$flare_child_5 = array("name"=> "subchild5", "size"=> 60);
$flare_children_1[] = $flare_child_1;
$flare_children_1[] = $flare_child_2;
$flare_children_1[] = $flare_child_3;
$flare_children_1[] = $flare_child_4;
$flare_children_1[] = $flare_child_5;
$flare_children[] = array('name'=> "first", 'children'=>$flare_children_1);
$flare = array('name'=> "flare", 'children'=>$flare_children);
echo "<script> var root = "; echo json_encode($flare); echo ";";
echo "input_data(root);</script>";
?>
</body>
js file
var width = 960,
height = 700,
radius = (Math.min(width, height) / 2) - 10;
var formatNumber = d3.format(",d");
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var color = d3.scale.category20c();
var partition = d3.layout.partition()
.value(function(d) { return d.size; });
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI,x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI,x(d.x + d.dx))); })
.innerRadius(function(d) { return Math.max(0, y(d.y)); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
function getRootmostAncestorByRecursion(node) {
return node.depth > 1 ? getRootmostAncestorByRecursion(node.parent) : node;
}
function input_data(root) {
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");
svg.selectAll("path")
.data(partition.nodes(root))
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) {
return color(getRootmostAncestorByRecursion(d).name);
})
.on("click", click)
.append("title")
.text(function(d) {
return d.name + "\n" + formatNumber(d.value);
});
}
function click(d) {
svg.transition()
.duration(750)
.tween("scale", function() {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); };
})
.selectAll("path")
.attrTween("d", function(d) { return function() { return arc(d);}; });
}
d3.select(self.frameElement).style("height", height + "px");
I'm expecting something similar to this https://bl.ocks.org/mbostock/raw/4348373/
but all I'm seeing a blue spot. The children don't feature. I'm not sure what I'm doing wrong.
Thanks for looking at this.
Couple things wrong here.
First, while you are correct path creation is wrapped in a function and will execute after the php code but your svg creation will execute before the body tag, so you'll never get an svg tag.
Second, your JSON is malformed. I executed the php and it produces:
<script>
var root = {
"name": "flare",
"children": {
"name": "first",
"children": [{
"name": "subchild1",
"size": 90
}, {
"name": "subchild2",
"size": 10
}, {
"name": "subchild3",
"size": 55
}, {
"name": "subchild4",
"size": 72
}, {
"name": "subchild5",
"size": 60
}]
}
};
input_data(root);
Notice, that the first children is an object and not an array of objects.
Putting these two things together here.

How can I alter the bezier function used by D3's diagonal()?

I'm creating an org chart in D3 based on Bernhard Zuba's D3.js Organizational Chart. The org chart models an organization in which any given person (represented by a white square) may have a hundred or so people immediately beneath them (a very flat tree structure with a black bezier curve representing each parent-child relationship).
Here's a screencap of part of the tree:
And here's a zoom-in on the bottom of the parent node in the above picture:
The problem is that the links between child and parent nodes tend to all bunch up together, resulting in a very thick black line with a very gradual slope, which can be a bit of an eyesore.
The function I'm using to generate the links is as follows:
// Diagonal function
var diagonal = d3.svg.diagonal()
.source(function (d) {
return {
x: d.source.x + (rectW / 2),
y: d.source.y + rectH - 10
};
})
.target(function (d) {
return {
x: d.target.x + (rectW / 2),
y: d.target.y + 10
};
})
.projection(function (d) {
return [d.x, d.y];
});
Here, rectW is the width of each node and rectH is the height of each node.
What I'd like to do is make some slight adjustments to the bezier function used to generate the links. Specifically, I'd like to flatten out the control points a little so that the curves at the start and end of the curve are more dramatic. If anyone can show me how to alter the function used by diagonal() to generate the bezier curve, I can figure out the rest.
If you look at the source code to svg.diagonal, I can't really see a direct way to adjust just the control points. You'd think you could use projection for this, but that'll transform all 4 points used to generate the path. Now, I guess we could get a little hacky with projection and do something like this:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
var data = [{
source: {
x: 10,
y: 10
},
target: {
x: 200,
y: 200
}
}, {
source: {
x: 50,
y: 50
},
target: {
x: 200,
y: 200
}
}];
var svg = d3.select('body')
.append('svg')
.attr('width', 205)
.attr('height', 205);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
if (!this.times) this.times = 0;
this.times++;
console.log(this.times);
if (this.times === 1) {
return [d.x, d.y];
} else if (this.times === 2) {
return [d.x - 25, d.y]
} else if (this.times === 3) {
return [d.x + 25, d.y];
} else if (this.times === 4) {
this.times = 0;
return [d.x, d.y];
}
});
svg.selectAll('path')
.data(data)
.enter()
.append('path')
.attr('d', diagonal)
.style('fill', 'none')
.style('stroke', 'black');
</script>
</body>
</html>
I might be over thinking this. You'd probably just be better drawing the arc yourself:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
var data = [{
source: {
x: 10,
y: 10
},
target: {
x: 200,
y: 200
}
}, {
source: {
x: 200,
y: 10
},
target: {
x: 10,
y: 200
}
}];
var svg = d3.select('body')
.append('svg')
.attr('width', 205)
.attr('height', 205);
svg.selectAll('path')
.data(data)
.enter()
.append('path')
.attr('d', function(d){
var s = d.source,
t = d.target,
m = (s.y + t.y) / 2,
p0 = [s.x, s.y],
p1 = [s.x, m],
p2 = [t.x, m],
p3 = [t.x, t.y];
// adjust constrol points
p1[0] -= 25;
p2[0] += 25;
return "M" + p0 + "C" + p1 + " " + p2 + " " + p3;
})
.style('fill', 'none')
.style('stroke', 'black');
</script>
</body>
</html>

D3.js Donut Chart change node colors and add text label to middle

My goal is to create an animated donut chart that shows 75% - 90% accuracy rate. For this I've started with the code below, but I'd like to make a few tweaks:
I would like to customize the colors of each node output by the
chart (I've added the variable section_path_fill_colors). Currently the code
just chooses random colors I believe.
I would like to add a static text label in the middle of the donut
75% - 90% (I've added the variable static_label).
Currently the labels are attached to each node.
Can someone help me accomplish this?
UPDATE:
I was able to solve the coloring of nodes with:
var color = d3.scale.ordinal()
.domain(["one", "two", "three"])
.range(["#ffffff" , "#d1d2d4" , "#17afd1"]);
Now just need help setting the static label in the middle
JS:
var static_label = '75% - 90%';
var employees = [
{dept: '', count : 75},
{dept: '', count : 15},
{dept: '', count : 10}
];
var color = d3.scale.ordinal()
.domain(["one", "two", "three"])
.range(["#ffffff" , "#d1d2d4" , "#17afd1"]);
var maxWidth = 200;
var maxHeight = 200;
var outerRadius = 100;
var ringWidth = 20;
function checkEndAll(transition, callback) {
var n = 0;
transition
.each(function() { ++n; })
.each("end", function() {
if (!--n) callback.apply(this, arguments);
});
}
function drawAnimatedRingChart(config) {
var pie = d3.layout.pie().value(function (d) {
return d.count;
});
//var color = d3.scale.category10();
var arc = d3.svg.arc();
function tweenPie(finish) {
var start = {
startAngle: 0,
endAngle: 0
};
var i = d3.interpolate(start, finish);
return function(d) { return arc(i(d)); };
}
arc.outerRadius(config.outerRadius || outerRadius)
.innerRadius(config.innerRadius || innerRadius);
// Remove the previous ring
d3.select(config.el).selectAll('g').remove();
var svg = d3.select(config.el)
.attr({
width : maxWidth,
height: maxHeight
});
// Add the groups that will hold the arcs
var groups = svg.selectAll('g.arc')
.data(pie(config.data))
.enter()
.append('g')
.attr({
'class': 'arc',
'transform': 'translate(' + outerRadius + ', ' + outerRadius + ')'
});
// Create the actual slices of the pie
groups.append('path')
.attr({
'fill': function (d, i) {
return color(i);
}
})
.transition()
.duration(config.duration || 1000)
.attrTween('d', tweenPie)
.call(checkEndAll, function () {
// Finally append the title of the text to the node
groups.append('text')
.attr({
'text-anchor': 'middle',
'transform': function (d) {
return 'translate(' + arc.centroid(d) + ')';
}
})
.text(function (d) {
// Notice the usage of d.data to access the raw data item
return d.data.dept;
});
});
}
// Render the initial ring
drawAnimatedRingChart({
el: '.animated-ring svg',
outerRadius: outerRadius,
innerRadius: outerRadius - ringWidth,
data: employees
});
Just do this:
svg.append('text')
.attr({
x: outerRadius,
y: outerRadius,
'text-anchor': 'middle
})
.text(static_label);

d3.js Pie Chart With label

I started working with this d3.js Donut Chart: JSFiddleI am trying to change it into a Pie Chart without the circle in the middle. I am new to d3.js. I have tried several different ideas but have been unable to get this to remove the circle in the middle of the chart. Any and all help is appreciated
Here is my code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<style>
.label-text {
alignment-baseline : middle;
font-size: 12px;
font-family: arial,helvetica,"sans-serif";
fill: #393939;
}
.label-line {
stroke-width: 1;
stroke: #393939;
}
.label-circle {
fill: #393939;
}
</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg>
<g id="canvas">
<g id="art" />
<g id="labels" /></g>
</svg>
<script>
var data = [{
label: 'Star Wars',
instances: 207
}, {
label: 'Lost In Space',
instances: 3
}, {
label: 'the Boston Pops',
instances: 20
}, {
label: 'Indiana Jones',
instances: 150
}, {
label: 'Harry Potter',
instances: 75
}, {
label: 'Jaws',
instances: 5
}, {
label: 'Lincoln',
instances: 1
}];
svg = d3.select("svg");
canvas = d3.select("#canvas");
art = d3.select("#art");
labels = d3.select("#labels");
// Create the pie layout function.
// This function will add convenience
// data to our existing data, like
// the start angle and end angle
// for each data element.
jhw_pie = d3.layout.pie();
jhw_pie.sort(null);
jhw_pie.value(function (d) {
// Tells the layout function what
// property of our data object to
// use as the value.
return d.instances;
});
// Store our chart dimensions
cDim = {
height: 500,
width: 500,
innerRadius: 50,
outerRadius: 150,
labelRadius: 175
}
// Set the size of our SVG element
svg.attr({
height: cDim.height,
width: cDim.width
});
// This translate property moves the origin of the group's coordinate
// space to the center of the SVG element, saving us translating every
// coordinate individually.
canvas.attr("transform", "translate(" + (cDim.width / 2) + "," + (cDim.height / 2) + ")");
pied_data = jhw_pie(data);
// The pied_arc function we make here will calculate the path
// information for each wedge based on the data set. This is
// used in the "d" attribute.
pied_arc = d3.svg.arc()
.innerRadius(50)
.outerRadius(150);
// This is an ordinal scale that returns 10 predefined colors.
// It is part of d3 core.
pied_colors = d3.scale.ordinal()
.range(["#04B486", "#F2F2F2", "#F5F6CE", "#00BFFF","orange","purple","pink"]);
// Let's start drawing the arcs.
enteringArcs = art.selectAll(".wedge").data(pied_data)
.enter();
enteringArcs
.append("g")
.attr("class", "wedge")
.append("path")
.attr("d", pied_arc)
.style("fill", function (d, i) {
return pied_colors(i);
});
// Now we'll draw our label lines, etc.
enteringLabels = labels.selectAll(".label").data(pied_data).enter();
labelGroups = enteringLabels.append("g").attr("class", "label");
labelGroups.append("circle").attr({
x: 0,
y: 0,
r: 2,
fill: "#000",
transform: function (d, i) {
centroid = pied_arc.centroid(d);
return "translate(" + pied_arc.centroid(d) + ")";
},
'class': "label-circle"
});
// "When am I ever going to use this?" I said in
// 10th grade trig.
textLines = labelGroups.append("line").attr({
x1: function (d, i) {
return pied_arc.centroid(d)[0];
},
y1: function (d, i) {
return pied_arc.centroid(d)[1];
},
x2: function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
x = Math.cos(midAngle) * cDim.labelRadius;
return x;
},
y2: function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
y = Math.sin(midAngle) * cDim.labelRadius;
return y;
},
'class': "label-line"
});
textLabels = labelGroups.append("text").attr({
x: function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
x = Math.cos(midAngle) * cDim.labelRadius;
sign = (x > 0) ? 1 : -1
labelX = x + (5 * sign)
return labelX;
},
y: function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
y = Math.sin(midAngle) * cDim.labelRadius;
return y;
},
'text-anchor': function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
x = Math.cos(midAngle) * cDim.labelRadius;
return (x > 0) ? "start" : "end";
},
'class': 'label-text'
}).text(function (d) {
return d.data.label
});
alpha = 0.5;
spacing = 12;
function relax() {
again = false;
textLabels.each(function (d, i) {
a = this;
da = d3.select(a);
y1 = da.attr("y");
textLabels.each(function (d, j) {
b = this;
// a & b are the same element and don't collide.
if (a == b) return;
db = d3.select(b);
// a & b are on opposite sides of the chart and
// don't collide
if (da.attr("text-anchor") != db.attr("text-anchor")) return;
// Now let's calculate the distance between
// these elements.
y2 = db.attr("y");
deltaY = y1 - y2;
// Our spacing is greater than our specified spacing,
// so they don't collide.
if (Math.abs(deltaY) > spacing) return;
// If the labels collide, we'll push each
// of the two labels up and down a little bit.
again = true;
sign = deltaY > 0 ? 1 : -1;
adjust = sign * alpha;
da.attr("y", +y1 + adjust);
db.attr("y", +y2 - adjust);
});
});
// Adjust our line leaders here
// so that they follow the labels.
if (again) {
labelElements = textLabels[0];
textLines.attr("y2", function (d, i) {
labelForLine = d3.select(labelElements[i]);
return labelForLine.attr("y");
});
setTimeout(relax, 20)
}
}
relax();
</script>
</body>
</html>
Thanks
See this updated fiddle.
The code contained the following lines, of which the innerRadious was changed to 0.
pied_arc = d3.svg.arc()
.innerRadius(00) // <- this
.outerRadius(150);
It's a bit misleading, as there's an innerRadius variable somewhere before that, but it's not used at this point. While you're at it, you might want to align all of that stuff.

D3 Voronoi Edge Condition

I'm working with a D3 example file for a force directed voronoi graph... however, I mainly just needed a simplified version with only three vertices... so I simplified the file and have included a JSFiddle example of where my file stands currently. My issue is how the edge condition is handled. Right now, the voronoi edges extend out to the edge of the container div. However, I'd like to clip each voronoi cell by a circle boundary. I've included two images to help explain my problem. The first image shows the script as it exists now, whereas the second is made with photoshop - showing the circular clipping boundary. It seems like D3's polygon.clip method would be the best option, but I don't really know how to implement the clipping method into my script. Any suggestions would be greatly appreciated.
<!DOCTYPE html>
<html>
<head>
<title>Voronoi Diagram with Force Directed Nodes and Delaunay Links</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
path
{
stroke: #EFEDF5;
stroke-width: 4px;
}
</style>
</head>
<body>
<div id="chart">
</div>
<script type="text/javascript">
var w = window.innerWidth > 960 ? 960 : (window.innerWidth || 960),
h = window.innerHeight > 500 ? 500 : (window.innerHeight || 500),
radius = 5.25,
links = [],
simulate = true,
zoomToAdd = true,
cc = ["#FFA94A","#F58A3A","#F85A19"]
var numVertices = (w*h) / 200000;
var vertices = d3.range(numVertices).map(function(i) {
angle = radius * (i+10);
return {x: angle*Math.cos(angle)+(w/2), y: angle*Math.sin(angle)+(h/2)};
});
var d3_geom_voronoi = d3.geom.voronoi().x(function(d) { return d.x;}).y(function(d) { return d.y; })
var prevEventScale = 1;
var zoom = d3.behavior.zoom().on("zoom", function(d,i) {
if (zoomToAdd){
if (d3.event.scale > prevEventScale) {
angle = radius * vertices.length;
}
force.nodes(vertices).start()
} else {
if (d3.event.scale > prevEventScale) {
radius+= .01
} else {
radius -= .01
}
vertices.forEach(function(d, i) {
angle = radius * (i+10);
vertices[i] = {x: angle*Math.cos(angle)+(w/2), y: angle*Math.sin(angle)+(h/2)};
});
force.nodes(vertices).start()
}
prevEventScale = d3.event.scale;
});
d3.select(window)
.on("keydown", function() {
// shift
if(d3.event.keyCode == 16) {
zoomToAdd = false
}
})
.on("keyup", function() {
zoomToAdd = true
})
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h)
.call(zoom)
var force = d3.layout.force()
.charge(-300)
.size([w, h])
.on("tick", update);
force.nodes(vertices).start();
var path = svg.selectAll("path");
function update(e) {
path = path.data(d3_geom_voronoi(vertices))
path.enter().append("path")
// drag node by dragging cell
.call(d3.behavior.drag()
.on("drag", function(d, i) {
vertices[i] = {x: vertices[i].x + d3.event.dx, y: vertices[i].y + d3.event.dy}
})
)
.style("fill", function(d, i) { return cc[0] })
path.attr("d", function(d) { return "M" + d.join("L") + "Z"; })
.transition().duration(150)
.style("fill", function(d, i) { return cc[i] })
path.exit().remove();
if(!simulate) force.stop()
}

Resources