|
var bounds = { |
|
width: 792, |
|
height: 612 |
|
}; |
|
|
|
var color = d3.scale.category20b(); |
|
|
|
var force = d3.layout.force() |
|
.charge(-120) |
|
.linkDistance(30) |
|
.size([bounds.width, bounds.height]); |
|
|
|
var nodeRadius = 7; |
|
|
|
function drawPointyArc(d) { |
|
var dx = d.target.x - d.source.x; |
|
var dy = d.target.y - d.source.y; |
|
var arcRadius = 10 * dx / Math.abs(dx); |
|
var theta; |
|
var edgePoint; |
|
var front; |
|
var back; |
|
var arc; |
|
|
|
if (dx === 0) { |
|
if (dy >= 0) { |
|
theta = Math.PI; |
|
} else { |
|
theta = -Math.PI; |
|
} |
|
edgePoint = { |
|
x: 0, |
|
y: nodeRadius |
|
}; |
|
} else { |
|
theta = Math.atan((d.target.y - d.source.y) / (d.target.x - d.source.x)) + Math.PI / 2; |
|
edgePoint = { |
|
x: nodeRadius * Math.cos(theta), |
|
y: nodeRadius * Math.sin(theta) |
|
}; |
|
} |
|
front = { |
|
x: d.source.x + edgePoint.x, |
|
y: d.source.y + edgePoint.y |
|
}; |
|
back = { |
|
x: d.source.x - edgePoint.x, |
|
y: d.source.y - edgePoint.y |
|
}; |
|
arc = { |
|
x: (d.source.x + d.target.x) / 2 + arcRadius * Math.cos(theta), |
|
y: (d.source.y + d.target.y) / 2 + arcRadius * Math.sin(theta) |
|
}; |
|
return 'M' + |
|
front.x + ',' + |
|
front.y + 'Q' + |
|
arc.x + ',' + |
|
arc.y + ',' + |
|
d.target.x + ',' + |
|
d.target.y + 'Q' + |
|
arc.x + ',' + |
|
arc.y + ',' + |
|
back.x + ',' + |
|
back.y + 'Z'; |
|
} |
|
|
|
d3.csv('matches.csv', function (data) { |
|
var skaterLookup = {}; |
|
|
|
var graph = { |
|
nodes: [], |
|
edges: [] |
|
}; |
|
|
|
data.sort(function (a, b) { |
|
return a.round - b.round; |
|
}); |
|
|
|
data.forEach(function (d) { |
|
if (!skaterLookup.hasOwnProperty(d.winner)) { |
|
skaterLookup[d.winner] = graph.nodes.length; |
|
graph.nodes.push({ |
|
name: d.winner, |
|
roundNumber: d.round, |
|
highestRound: true |
|
}); |
|
} |
|
if (!skaterLookup.hasOwnProperty(d.loser)) { |
|
skaterLookup[d.loser] = graph.nodes.length; |
|
graph.nodes.push({ |
|
name: d.loser, |
|
roundNumber: d.round, |
|
highestRound: true |
|
}); |
|
} |
|
var oldWinner = skaterLookup[d.winner]; |
|
graph.nodes[oldWinner].highestRound = false; |
|
skaterLookup[d.winner] = graph.nodes.length; |
|
graph.nodes.push({ |
|
name: d.winner, |
|
roundNumber: d.round, |
|
highestRound: true |
|
}); |
|
graph.edges.push({ |
|
source: oldWinner, |
|
target: skaterLookup[d.winner] |
|
}); |
|
graph.edges.push({ |
|
source: skaterLookup[d.loser], |
|
target: skaterLookup[d.winner] |
|
}); |
|
}); |
|
|
|
var linkLayer = d3.select('#Layer_1'); |
|
var nodeLayer = d3.select('#Layer_2'); |
|
|
|
force.nodes(graph.nodes) |
|
.links(graph.edges) |
|
.start(); |
|
|
|
var link = linkLayer.selectAll('.link') |
|
.data(graph.edges); |
|
var linkEnter = link.enter(); |
|
linkEnter.append('path') |
|
.attr('class', 'link'); |
|
|
|
var node = nodeLayer.selectAll('.node') |
|
.data(graph.nodes); |
|
var nodeEnter = node.enter().append('g') |
|
.attr('class', 'node') |
|
.attr('id', function (d, i) { |
|
if (d.highestRound === false) { |
|
return d.name + d.roundNumber; |
|
} else { |
|
return d.name; |
|
} |
|
}); |
|
|
|
nodeEnter.append('rect') |
|
.attr('x', '-0.75em') |
|
.attr('width', '1.5em') |
|
.attr('y', '-1em') |
|
.attr('height', '1.5em') |
|
.attr('fill', function (d) { |
|
return color(d.name); |
|
}); |
|
|
|
node.call(force.drag); |
|
|
|
force.on('tick', function (e) { |
|
link.attr('d', drawPointyArc); |
|
|
|
node.attr('transform', function (d) { |
|
return 'translate(' + d.x + ',' + d.y + ')'; |
|
}); |
|
}); |
|
}); |