Created
June 11, 2014 14:11
-
-
Save jfensign/0ac827ebb568605f2f49 to your computer and use it in GitHub Desktop.
D3 force-directed graph Class
This file contains hidden or 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
| function forceGraph(opts) { | |
| opts = opts || {}; | |
| opts.node = opts.node || {}; | |
| opts.link = opts.link || {}; | |
| var | |
| Self = this, | |
| defaults = { | |
| el: opts.el, | |
| node: { | |
| click: opts.node.click || function(d) { | |
| console.log(d); | |
| }, | |
| dblclick: opts.node.dblclick || function(d) { | |
| console.log(d); | |
| }, | |
| gravity: opts.node.gravity || 2.8 | |
| }, | |
| link: { | |
| click: opts.link.click || function(d) { | |
| console.log(d); | |
| }, | |
| dblclick: opts.link.dblclick || function(d) { | |
| console.log(d); | |
| }, | |
| distance: opts.link.distance || 25 | |
| } | |
| }, | |
| extend = function() { | |
| _.extend(defaults, opts) | |
| }(); | |
| this.set = function(obj) { | |
| _.extend(defaults, obj) | |
| return Self.update(); | |
| }; | |
| this.addNode = function (opts) { | |
| nodes.push(opts) | |
| return Self.update(); | |
| }; | |
| this.hideLinks = function() { | |
| $(".link").hide() | |
| }; | |
| this.showLinks = function() { | |
| $(".link").show() | |
| return Self; | |
| }; | |
| this.removeNode = function (id) { | |
| var | |
| i = 0, | |
| n = findNode(id); | |
| while (i < links.length) { | |
| if((links[i]['source'] == n)|| | |
| (links[i]['target'] == n)) { | |
| links.splice(i,1) | |
| } | |
| else ++i; | |
| } | |
| nodes.splice(findNodeIndex(id),1); | |
| return Self.update(); | |
| } | |
| this.addLink = function (source, target, type) { | |
| links.push({ | |
| "source":findNode(source), | |
| "target":findNode(target), | |
| "type": type || null | |
| }); | |
| return Self.update(); | |
| } | |
| this.getNodes = function() { | |
| return Self.nodes; | |
| } | |
| this.getLinks = function() { | |
| return Self.links; | |
| } | |
| this.clearLinks = function() { | |
| Self.links.length = 0; | |
| return Self; | |
| } | |
| var | |
| findLink = this.findLink = function(source, target) { | |
| if(links) { | |
| for(var i in links) { | |
| if(links[i].source == source && links[i].target == target) | |
| return links[i]; | |
| } | |
| } | |
| }, | |
| findNode = this.findNode = function(id) { | |
| for (var i in nodes) { | |
| if (nodes[i]["id"] === id) | |
| return nodes[i] | |
| } | |
| }, | |
| findNodeIndex = this.findNodeIndex = function(id) { | |
| for (var i in nodes) { | |
| if (nodes[i]["id"] === id) | |
| return i; | |
| } | |
| }, | |
| w = $(defaults.el).innerWidth(), | |
| h = $(defaults.el).innerHeight(), | |
| markerWidth = 6, | |
| markerHeight = 6, | |
| cRadius = 10, | |
| refX = cRadius + (markerWidth * 1.2), | |
| refY = -Math.sqrt(cRadius), | |
| drSub = cRadius + refY, | |
| vis = this.vis = d3. | |
| select(defaults.el). | |
| append("svg:svg"). | |
| attr("width", w). | |
| attr("height", h), | |
| force = this.force = d3. | |
| layout. | |
| force(). | |
| gravity(defaults.node.gravity || 4.8). | |
| linkDistance(defaults.link.distance || 25). | |
| charge(-4000). | |
| size([w, h]), | |
| nodes = this.nodes = force.nodes(), | |
| links = this.links = force.links(); | |
| vis. | |
| append("svg:defs"). | |
| selectAll("marker"). | |
| data(["forward", "backward", "resolved"]). | |
| enter().append("svg:marker"). | |
| attr("id", String). | |
| attr("viewBox", "5 -5 8 12"). | |
| attr("refX", refX). | |
| attr("refY", refY). | |
| attr("markerWidth", markerWidth). | |
| attr("markerHeight", markerHeight). | |
| attr("orient", "auto"). | |
| append("path"). | |
| attr("d", "M0,-5L10,0L0,5") | |
| this.update = function () { | |
| var | |
| node = vis. | |
| selectAll("circle"). | |
| data(d3.values(nodes), function(d) { | |
| return d.id | |
| }). | |
| on("click", defaults.node.click), | |
| nodeEnter = node. | |
| enter(). | |
| insert("g"). | |
| attr("class", "node"). | |
| call(force.drag); | |
| nodeEnter. | |
| append("circle"). | |
| attr("r", function(d) { | |
| var | |
| max = 50, | |
| t_two = (d.contentCount * 2); | |
| return ((d.contentCount === 0 || !d.contentCount) | |
| ? 1 | |
| : t_two>max?max:t_two) | |
| }). | |
| attr("fill", function(d) { | |
| return d.color || "#CCC" | |
| }). | |
| attr("id", function(d) { | |
| return d._id | |
| }); | |
| node.exit().remove(); | |
| var | |
| link = vis. | |
| selectAll("path.link"). | |
| data(links, function(d) { | |
| return d.source.id + "-" + d.target.id; | |
| }), | |
| linkEnter = link. | |
| enter(). | |
| insert("path"). | |
| attr("class", function (d) { | |
| return d.type | |
| ? ("link " + (d.type == "document" ? d.type : "")) | |
| : ("link " + (d.target.index < d.source.index | |
| ? "backward" | |
| : "forward" )) | |
| }). | |
| attr("marker-end", function (d) { | |
| return d.type | |
| ? "" | |
| : "url(#" + (d.target.index < d.source.index | |
| ? "backward" | |
| : "forward") + ")" | |
| }). | |
| style("stroke-width", 1.0). | |
| attr("stroke-dasharray", 5); | |
| link.exit().remove() | |
| var | |
| text = vis. | |
| selectAll("text"). | |
| data(force.nodes()), | |
| textEnter = text.enter(); | |
| textEnter. | |
| append("text"). | |
| attr("x", 0). | |
| attr("y", ".51em"). | |
| attr("class", "nodeText"). | |
| text(function (d) { | |
| return (d.id + (d.contentCount | |
| ? "(" + d.contentCount + ")" | |
| : "")) | |
| }); | |
| text.exit().remove(); | |
| force. | |
| on("tick", function() { | |
| link.attr("d", function(d) { | |
| var | |
| dx = d.target.x - d.source.x, | |
| dy = d.target.y - d.source.y, | |
| dr = Math.sqrt(dx * dx + dy * dy), | |
| val = [ | |
| ("M" + d.source.x), | |
| (d.source.y + "A" + dr), | |
| (dr + " 0 0,1 " + d.target.x), | |
| d.target.y | |
| ]. | |
| join(","); | |
| return val; | |
| }); | |
| node. | |
| attr("transform", function(d) { | |
| return "translate(" + d.x + "," + d.y + ")"; | |
| }); | |
| text. | |
| attr("transform", function(d) { | |
| return "translate(" + d.x + "," + d.y + ")" | |
| }) | |
| }). | |
| start(); | |
| setTimeout(function() { force.stop(); }, 2000); | |
| return Self; | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment