Skip to content

Instantly share code, notes, and snippets.

@musesum
Created July 12, 2016 21:59
Show Gist options
  • Save musesum/3b0872d557a811656d2848a32b9e71e8 to your computer and use it in GitHub Desktop.
Save musesum/3b0872d557a811656d2848a32b9e71e8 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<meta charset="utf-8">
<canvas></canvas>
<title>Tr3 D3 Test</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
/* expanded tr3 graph
a{b1 b2}:{c1 c2}
a^c1->a^c2
*/
var graph = {
'nodes': [
{'id':1, 'name':'√','children':[2,], },
{'id':2, 'name':'a','children':[11,14,], },
{'id':11, 'name':'b1','children':[12,13,], },
{'id':12, 'name':'c1',"edges":['12.13','12.16',],},
{'id':13, 'name':'c2',"edges":['12.13','15.13',],},
{'id':14, 'name':'b2','children':[15,16,], },
{'id':15, 'name':'c1',"edges":['15.13','15.16',],},
{'id':16, 'name':'c2',"edges":['12.16','15.16',],},
],
'links': [
{'id':'1.2' ,'source':1, 'target':2, 'type':0},
{'id':'2.11' ,'source':2, 'target':11, 'type':0},
{'id':'11.12' ,'source':11, 'target':12, 'type':0},
{'id':'11.13' ,'source':11, 'target':13, 'type':0},
{'id':'2.14' ,'source':2, 'target':14, 'type':0},
{'id':'14.15' ,'source':14, 'target':15, 'type':0},
{'id':'14.16' ,'source':14, 'target':16, 'type':0},
{'id':'12.13' ,'source':12, 'target':13, 'type':2},
{'id':'12.16' ,'source':12, 'target':16, 'type':2},
{'id':'15.13' ,'source':15, 'target':13, 'type':2},
{'id':'15.16' ,'source':15, 'target':16, 'type':2},
]
};
// canvas ------------------------------------------------------
var width = window.innerWidth;
var height = window.innerHeight;
var canvasDoc = document.querySelector("canvas");
canvasDoc.width = width;
canvasDoc.height = height;
var canvas = d3.select("canvas"),
context = canvas.node().getContext("2d"),
radius = 8,
transform = d3.zoomIdentity;
canvas
.call(d3.drag()
.subject(dragsubject)
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.call(d3.zoom()
.scaleExtent([0.5, 8])
.on("zoom", zoomed))
.call(initMaps);
function zoomed() {
transform = d3.event.transform;
render();
}
// maps -------------------------------------------
var nodeMap, edgeMap;
function initMaps() {
nodeMap = {};
edgeMap = {};
graph.nodes.forEach(function (d) { nodeMap[d.id] = d; });
graph.links.forEach(function (d) { edgeMap[d.id] = d; });
}
// force ------------------------------------------------------
var huh = d3.forceX();
var forces = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(-18))
.force("link", d3.forceLink().iterations(4).id(function(d) { return d.id; }))
.force("x", d3.forceX())
.force("y", d3.forceY())
.force("center", d3.forceCenter(width/2, height/2))
.on("tick", render);
// render ------------------------------------------------------
function render() {
// recalc nodes and links
var degreeOfInterest = 1;
var [nodes, edges] = flatten(graph.nodes[0], degreeOfInterest)
var root = nodes[0];
forces
.nodes(nodes)
.force("link")
.links(edges);
// set drawing context
context.save();
context.clearRect(0, 0, width, height);
context.translate(transform.x, transform.y);
context.scale(transform.k, transform.k);
context.font = "5px helvetica neue";
context.textAlign = 'center';
context.textBaseline = 'middle';
// draw edges first
drawLinks(edges);
drawNodes(nodes);
context.restore();
}
// Returns a list of all nodes under the root.
function flatten(root,doi) {
var nodes = [];
var edges = [];
function recurse(node,doi) {
nodes.push(node);
if (doi>0 || node.show) {
if (node.children) {
for (var i=0; i < node.children.length; i++) {
var childId = node.children[i];
var child = nodeMap[childId];
if (!child.x) {
child.x = node.x;
child.y = node.y;
}
var key = node.id + '.' + childId;
var edge = edgeMap[key];
edges.push(edge);
if (child.edges) {
for (var j=0; j < child.edges.length; j++) {
var key2 = child.edges[j];
var edge2 = edgeMap[key2];
edges.push(edge2);
}
}
recurse(child,doi-1);
}
}
}
}
recurse(root,doi);
return [nodes,edges];
}
function drawLinks(links) {
context.globalAlpha = 0.5;
for (var i=0; i < links.length; i++) {
var link = links[i];
var src = link.source.id ? nodeMap[link.source.id] : link.source;
var dst = link.target.id ? nodeMap[link.target.id] : link.target;
context.beginPath();
context.strokeStyle = (link.type ? "#000" :"#888");
context.moveTo(src.x, src.y);
context.lineTo(dst.x, dst.y);
context.stroke();
}
}
function drawNodes(nodes) {
for (var i = 0; i < nodes.length ; i++ ) {
var node = nodes[i];
context.globalAlpha = 1.0;
context.fillStyle = 'bbb';
context.strokeStyle = 'white';
context.beginPath();
context.moveTo(node.x + radius, node.y);
context.ellipse(node.x, node.y, radius, radius, 0, 0, 2 * Math.PI);
context.stroke();
context.fill();
drawText(node);
}
}
function drawText(d) {
context.beginPath();
context.fillStyle = 'black';
context.fillText(d.name,d.x,d.y);
}
// drag ------------------------------------------------------
function dragsubject() {
var x = transform.invertX(d3.event.x);
var y = transform.invertY(d3.event.y);
var found = forces.find(x,y);
var dx = x - found.x;
var dy = y - found.y;
if (dx * dx + dy * dy < radius * radius * 1.6) {
found.x = transform.applyX(found.x);
found.y = transform.applyY(found.y);
return found;
}
}
function dragstarted() {
if (!d3.event.active) {
forces.alphaTarget(0.3).restart();
}
if (d3.event.subject) {
var s = d3.event.subject;
s.x = transform.invertX(d3.event.x);
s.y = transform.invertY(d3.event.y);
s.startX = s.x;
s.startY = s.y;
s.fx = s.x;
s.fy = s.y;
}
}
function dragged() {
if (d3.event.subject) {
var s = d3.event.subject;
s.x = transform.invertX(d3.event.x);
s.y = transform.invertY(d3.event.y);
s.fx = s.x;
s.fy = s.y;
render();
}
}
function dragended() {
if (!d3.event.active) {
forces.alphaTarget(0.3);
}
if (d3.event.subject) {
var s = d3.event.subject;
s.x = transform.invertX(d3.event.x);
s.y = transform.invertY(d3.event.y);
var dx = s.startX - s.x,
dy = s.startY - s.y;
if (dx * dx + dy * dy < radius * radius * 1.6) {
s.fx = null;
s.fy = null;
}
if (s.children) {
s.show = s.show ? false : true;
render();
}
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment