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('path') .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 + ')'; }); }); });