Skip to content

Instantly share code, notes, and snippets.

@ericcoopey
Last active December 21, 2015 22:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericcoopey/6372768 to your computer and use it in GitHub Desktop.
Save ericcoopey/6372768 to your computer and use it in GitHub Desktop.
Matrix with Multi-Foci Force Graph

Using D3 to visualize commenting activity in a single depth threaded forum designed for education. Users can post a top level idea, respond to themself, or respond to others. Each Activity has a list of Prompts to respond to, and this gives the teacher a quick overview of user activity.

Demonstrates how to do several things in D3

  1. How to create a matrix
  2. Multi-Foci force graph, as found here, http://bl.ocks.org/mbostock/1021953
  3. Dynamic SVG size based on the size of the input
<!DOCTYPE html>
<html>
<head>
<title>D3 Matrix Example</title>
<script src="http://d3js.org/d3.v2.min.js"></script>
</head>
<style>
body {
font-family:"Lucida Grande","Droid Sans",Arial,Helvetica,sans-serif;
}
.axis path,
.major line {
fill: none;
stroke: black;
stroke-width: 0;
shape-rendering: crispEdges;
}
.minor {
fill: none;
stroke: #C0C0C0;
stroke-width: 1;
shape-rendering: crispEdges;
}
.legend {
border: 1px solid #555555;
border-radius: 5px 5px 5px 5px;
font-size: 0.8em;
margin: 10px;
padding: 8px;
}
.axis text {
font-size: 0.9em;
}
.bld {
font-weight: bold;
}
</style>
<body>
</body>
<script>
var raw = '{"responses":[{"newidea":"1","self":"0","grpIndex":"0","quIndex":0},{"newidea":"0","self":"0","grpIndex":"0","quIndex":1},{"newidea":"1","self":"0","grpIndex":"5","quIndex":0},{"newidea":"1","self":"0","grpIndex":"6","quIndex":0},{"newidea":"1","self":"0","grpIndex":"3","quIndex":0},{"newidea":"0","self":"1","grpIndex":"6","quIndex":0},{"newidea":"0","self":"0","grpIndex":"6","quIndex":1},{"newidea":"1","self":"0","grpIndex":"2","quIndex":1},{"newidea":"0","self":"1","grpIndex":"6","quIndex":1},{"newidea":"1","self":"0","grpIndex":"4","quIndex":1},{"newidea":"1","self":"0","grpIndex":"6","quIndex":1},{"newidea":"0","self":"1","grpIndex":"4","quIndex":1},{"newidea":"1","self":"0","grpIndex":"1","quIndex":1},{"newidea":"0","self":"0","grpIndex":"6","quIndex":2},{"newidea":"0","self":"0","grpIndex":"2","quIndex":2},{"newidea":"1","self":"0","grpIndex":"6","quIndex":2},{"newidea":"1","self":"0","grpIndex":"6","quIndex":2},{"newidea":"1","self":"0","grpIndex":"2","quIndex":2},{"newidea":"1","self":"0","grpIndex":"6","quIndex":2}],"groups":[{"group_name":"Bob","type":"g","indx":"0"},{"group_name":"Sally","type":"g","indx":"1"},{"group_name":"Jim","type":"g","indx":"2"},{"group_name":"Susan","type":"g","indx":"3"},{"group_name":"Tom","type":"g","indx":"4"},{"id":"100","group_name":"Frank","type":"u","indx":"5"},{"id":"14","group_name":"Christina","type":"u","indx":"6"}],"questions":[{"indx":0,"title":"Hypothesis"},{"indx":1,"title":"Results"},{"indx":2,"title":"Conclusion"}]}';
var json =JSON.parse(raw);
var w = 900,
h = 600,
lineheight = 50,
colwidth = 60,
leftpad = 120,
toppad = 50,
axispad = 40,
titlelength = 10;
// list of all posts
var nodes = json.responses;
// dynamically calculate height and width based on the input
h = (json.groups.length * lineheight) + toppad;
w = (json.questions.length * colwidth) + leftpad;
// create svg
var svg = d3.select("body").append("svg:svg")
.attr("width", w + leftpad)
.attr("height", h + toppad);
// initialize force graph
var force = d3.layout.force()
.nodes(nodes)
.links([])
.gravity(0)
.charge(-40)
.size([w, h])
.start();
// scale for axis
var xScale = d3.scale.linear()
.domain([0, json.questions.length-1])
.range([leftpad + axispad, w + axispad]);
var yScale = d3.scale.linear()
.domain([0, json.groups.length-1])
.range([h, toppad + axispad]);
// individual axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("top")
.ticks(json.questions.length-1)
.tickSubdivide(true)
.tickSize(-h+axispad/2)
.tickFormat(function(d,i){
return json.questions[i].title.substr(0, titlelength);
});
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(json.groups.length-1)
.tickSubdivide(true)
.tickSize(-w+axispad)
.tickFormat(function(d,i){
return json.groups[i].group_name;
});
// create the actual circles
var node = svg.selectAll(".node")
.data(json.responses)
.enter().append("circle")
.attr("class", "node")
.attr("cx", function(d) {
return xScale(d.quIndex);
})
.attr("cy", function(d) {
return yScale(d.grpIndex);
})
.attr("r", 8)
.style("fill", function(d,i) {
// determines the color
return d.newidea==1 ? 'green' : d.self==1 ? 'yellow' : 'blue';
})
.style("stroke", "#555")
.call(force.drag);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (toppad) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + leftpad + ",0)")
.call(yAxis);
// text of X axis
svg.append("text")
.attr("y", 15)
.attr("x",w/2 + 50)
.attr("class", "bld")
.text("Prompt");
// Text of Y axis, rotate it sideways
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 10)
.attr("x",-h/2-toppad)
.attr("class", "bld")
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("User");
force.on("tick", function(e) {
// Push nodes toward their designated focus.
var k = .55 * e.alpha;
nodes.forEach(function(o, i) {
// sets the focus of each node to the intersection of the scaled x and y location
// this is what makes the force graph multi-focal
o.y += (yScale(o.grpIndex) - o.y) * k;
o.x += (xScale(o.quIndex) - o.x) * k;
});
svg.selectAll("circle.node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
</script>
<br/>
<div class="legend" style="width: 400px; float: left;">
<svg height="20" width="500">
<circle cx="10" cy="10" r="8" fill="green"/>
<text x="25" y="15">New Ideas</text>
<circle cx="115" cy="10" r="8" fill="blue"/>
<text x="130" y="15">Respond to Others</text>
<circle cx="260" cy="10" r="8" fill="yellow"/>
<text x="275" y="15">Respond to Myself</text>
</svg>
</div>
<br><br>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment