Skip to content

Instantly share code, notes, and snippets.

@samuelleach
Last active December 16, 2015 21:09
Show Gist options
  • Save samuelleach/5497403 to your computer and use it in GitHub Desktop.
Save samuelleach/5497403 to your computer and use it in GitHub Desktop.
Force layout graph with colour-coded node neighbours.

First attempt at making a colour-coded graph, with code snippets inspired from the D3 community.

Green => Friend and follower.

Red => Follower

Blue => Friend.

Improvements needed:

  1. Smooth transition on the arrow head.

  2. Better positioning of the arrow heads.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node circle {
stroke: white;
stroke-width: 1.5px;
opacity: 1.0;
}
line {
stroke: black;
stroke-width: 1.5px;
stroke-opacity: 1.0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
data = {
nodes: [
{size: 10},
{size: 5},
{size: 2},
{size: 3},
{size: 30},
{size: 40}
],
links: [
{source: 0,target: 1},
{source: 0,target: 2},
{source: 1,target: 0},
{source: 3,target: 0},
{source: 4,target: 1}
]
}
var mouseOverFunction = function(d) {
var circle = d3.select(this);
node
.transition(500)
.style("opacity", function(o) {
return isConnected(o, d) ? 1.0 : 0.2 ;
})
.style("fill", function(o) {
if (isConnectedAsTarget(o, d) && isConnectedAsSource(o, d) ) {
fillcolor = 'green';
} else if (isConnectedAsSource(o, d)) {
fillcolor = 'red';
} else if (isConnectedAsTarget(o, d)) {
fillcolor = 'blue';
} else if (isEqual(o, d)) {
fillcolor = "hotpink";
} else {
fillcolor = '#000';
}
return fillcolor;
});
link
.transition(500)
.style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : 0.2;
})
.transition(500)
.attr("marker-end", function(o) {
return o.source === d || o.target === d ? "url(#arrowhead)" : "url()";
});
circle
.transition(500)
.attr("r", function(){ return 1.4 * node_radius(d)});
}
var mouseOutFunction = function() {
var circle = d3.select(this);
node
.transition(500);
link
.transition(500);
circle
.transition(500)
.attr("r", node_radius);
}
function isConnected(a, b) {
return isConnectedAsTarget(a, b) || isConnectedAsSource(a, b) || a.index == b.index;
}
function isConnectedAsSource(a, b) {
return linkedByIndex[a.index + "," + b.index];
}
function isConnectedAsTarget(a, b) {
return linkedByIndex[b.index + "," + a.index];
}
function isEqual(a, b) {
return a.index == b.index;
}
function tick() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
function node_radius(d) { return Math.pow(40.0 * d.size, 1/3); }
var width = 1000;
var height = 500;
var nodes = data.nodes
var links = data.links
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.charge(-3000)
.friction(0.6)
.gravity(0.6)
.size([width,height])
.start();
var linkedByIndex = {};
links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = true;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var link = svg.selectAll("line")
.data(links)
.enter().append("line")
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node
.append("circle")
.attr("r", node_radius)
.on("mouseover", mouseOverFunction)
.on("mouseout", mouseOutFunction);
svg
.append("marker")
.attr("id", "arrowhead")
.attr("refX", 6 + 7) // Controls the shift of the arrow head along the path
.attr("refY", 2)
.attr("markerWidth", 6)
.attr("markerHeight", 4)
.attr("orient", "auto")
.append("path")
.attr("d", "M 0,0 V 4 L6,2 Z");
link
.attr("marker-end", "url()");
force
.on("tick",tick);
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment