Created
July 12, 2016 21:59
-
-
Save musesum/3b0872d557a811656d2848a32b9e71e8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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