diff --git a/network-onefile.html b/network-onefile.html index 221f345..a5a4f2a 100644 --- a/network-onefile.html +++ b/network-onefile.html @@ -85,9 +85,8 @@ .attr("height", "800px") .attr("width", "90%"); -var nodes = [] var g_edges = []; -var box_width=200; // variable that determines box width and text length +var box_width=100; // variable that determines box width and text length function forceDirected() { queue() @@ -98,12 +97,17 @@ function createForceLayout(edges, group) { var nodeHash = {}; + var nodes = [] + var labelAnchorLinks = [] + var labelAnchors= [] // helper function to populate our node list function add_node(name, group, edge_id) { if ( nodeHash[name] === undefined ) { nodeHash[name] ={id: name, group: group, edges: []} ; nodes.push(nodeHash[name]); + labelAnchors.push({ node : nodeHash[name]}) + labelAnchors.push({ node : nodeHash[name]}) } nodeHash[name].edges.push(edge_id); } @@ -120,20 +124,58 @@ g_edges.push(edges[x]); } +for(var i = 0; i < nodes.length; i++) { + //labelAnchors.push({ node : nodeHash[i] }); + //labelAnchors.push({ node : nodeHash[i] }); + labelAnchorLinks.push({ + source : i * 2, + target : i * 2 + 1, + weight : 1 + }); +} var weightScale = d3.scale.linear() .domain(d3.extent(edges, function(d) {return d.weight})) .range([.1,1]); // defaults to weight = 1 force = d3.layout.force() - .charge(-1200) // #a How much each node pushes away each other, if set to a positive value, nodes will attract each other + .charge(-3000) // #a How much each node pushes away each other, if set to a positive value, nodes will attract each other + .gravity(1.00) + .friction(.50) + .linkDistance(50) +// function(d,i) { +// console.log(d,i); +// return 30 + (i%3)*20; +// } +// ) .size([800,800]) // size of whole animation width and height .nodes(nodes) .links(edges) .on("tick", forceTick); // #b “tick” events are fired continuously, running the associated function - d3.select("svg").selectAll("line.link") +// dense connection for labels, so they all repel each other + +force.start(); + +force2 = d3.layout.force() + .nodes(labelAnchors) + .links(labelAnchorLinks) + .gravity(0.010) + .linkDistance(0) + .linkStrength(8) + .charge(-120) // #a How much each node pushes away each other, if set to a positive value, nodes will attract each other +// function(d,i) { +// console.log(d,i); +// return 30 + (i%3)*20; +// } +// ) + .size([800,800]) // size of whole animation width and height +//.on("tick", forceTick); + + force2.start() + + var link = d3.select("svg").selectAll("line.link") .data(edges, function (d) {return d.source.id + "-" + d.target.id}) // #c Key values for our nodes and edges will help when we update the network later .enter() .append("line") @@ -141,13 +183,12 @@ .style("stroke-width", function(d) {return d.weight}); var nodeEnter = d3.select("svg").selectAll("g.node") -.data(nodes, function (d) {return d.id}) +.data(force.nodes(), function (d) {return d.id}) .enter() .append("g") .attr("class", "node"); //console.log (nodeEnter); // shows all 27 nodes - nodeEnter.append("circle") .attr("r", 5) @@ -183,77 +224,133 @@ function (e,j) { return e.highlight }); } + var anchorLink = svg.selectAll("line.anchorLink").data(labelAnchorLinks)//.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#F00"); + + var anchorNode = svg.selectAll("g.anchorNode").data(force2.nodes()).enter().append("svg:g").attr("class", "anchorNode"); + anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF"); + anchorNode.append("svg:text").text(function(d, i) { + console.log(d,i); + return i % 2 == 0 ? "" : d.node.id + }).style("fill", "#555").style("font-family", "Arial").style("font-size", 12); // replace circle with rectangular - nodeEnter.append('rect') - .attr('x', -box_width/2) - .attr('y', -5) - .attr('rx', 5) - .attr('height', '20') - .attr('width' , box_width) - .attr("class", function (d) {return d.group}) - - .on("mouseover", function(d,i) { - //d3.select(this).attr('title', d.id) svg:title takes care of this - //console.log(d, d.edges); - highlight(d.edges); - - - // return d.value ; - - }) - .on("mouseout", function(d,i) { - var rect = d3.select(this); - //rect.style("class", function( - highlight(d.edges, false); - - - // return d.value ; - - }) - - .on("click", function(d,i) { - console.log(d, this.nid); - highlight(d.edges, false); - }); - - ; - nodeEnter.append("text") - .style("text-anchor", "middle") - .style("pointer-events", "none") - .attr("y", 7) -// .attr("dy", ".1em") - .text(function(d) {return d.id}) - .attr("class", "text"); + var updateLink = function() { + this.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; + }); + + } + + var updateNode = function() { + this.attr("transform", function(d) { + return "translate(" + d.x + "," + d.y + ")"; + }); + + } - force.start(); // #d Initializing the network will start firing “tick” events as well as calculate the degree centrality of nodes + + nodeEnter.call(force.drag); + + //nodeEnter.append('rect') + // .attr('x', -box_width/2) + // .attr('y', -5) + // .attr('rx', 5) + // .attr('opacity', 0.5) + // .attr('height', '20') + // .attr('width' , box_width) + // .attr("class", function (d) {return d.group}) + // + // .on("mouseover", function(d,i) { + // //d3.select(this).attr('title', d.id) svg:title takes care of this + // //console.log(d, d.edges); + // highlight(d.edges); + // }) + // .on("mouseout", function(d,i) { + // var rect = d3.select(this); + // //rect.style("class", function( + // highlight(d.edges, false); + // }) + // + // .on("click", function(d,i) { + // console.log(d, this.nid); + // highlight(d.edges, false); + // }); + // + // ; + + + //nodeEnter.append("text") + //.style("text-anchor", "middle") + //.style("pointer-events", "none") + //.attr("y", 7) +// //.attr("dy", ".1em") + //.text(function(d) {return d.id}) + //.attr("class", "text"); + force.start(); // #d Initializing the network will start firing “tick” events as well as calculate the degree centrality of nodes function forceTick() { - d3.selectAll("line.link") - .attr("x1", function (d) {return d.source.x}) // #e The tick function updates the edge drawing code and node drawing code based on the newly calculated node positions - .attr("x2", function (d) {return d.target.x}) - .attr("y1", function (d) {return d.source.y}) - .attr("y2", function (d) {return d.target.y}); - - - d3.selectAll("g.node") - .attr("transform", function (d) {return "translate("+d.x+","+d.y+")"}) - } +// force2.start(); +// d3.selectAll("line.link") +// .attr("x1", function (d) {return d.source.x}) // #e The tick function updates the edge drawing code and node drawing code based on the newly calculated node positions +// .attr("x2", function (d) {return d.target.x}) +// .attr("y1", function (d) {return d.source.y}) +// .attr("y2", function (d) {return d.target.y}); +// +// +// d3.selectAll("g.node") +// .attr("transform", function (d) {return "translate("+d.x+","+d.y+")"}) +// } + + force2.start(); + + nodeEnter.call(updateNode); + + anchorNode.each(function(d, i) { + if(i % 2 == 0) { + d.x = d.node.x; + d.y = d.node.y; + } else { + var b = this.childNodes[1].getBBox(); + + var diffX = d.x - d.node.x; + var diffY = d.y - d.node.y; + + var dist = Math.sqrt(diffX * diffX + diffY * diffY); + + var shiftX = b.width * (diffX - dist) / (dist * 2); + shiftX = Math.max(-b.width, Math.min(0, shiftX)); + var shiftY = 5; + this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")"); + } + }); + + + anchorNode.call(updateNode); + + link.call(updateLink); + anchorLink.call(updateLink); + } pi_wrap(); } // helper function to drop all but the first set of characters which will fit // inside the box, and adds and ellipsis to the end function pi_wrap() { - var text = d3.selectAll('text')[0]; + var text = d3.selectAll('g.node text')[0]; text.forEach(function(x,i ) { while (x.getBBox().width > box_width - 20) { // keep dropping one letter off the end until we're down to 200 pixels x.textContent = x.textContent.slice(0, x.textContent.length-2) //x.textContent += "…" - x.textContent += '\u2026' + x.textContent += '\u2026'; } }); };