/* * why.js * A visualization framework for the D3 of our member-product graphs. * * Author: Jason Chong Lee * Date: Tue Oct 08 14:17:06 2013 -0400 * * Edited: Thu Oct 17 2013 * Desc: Added edges to graph; Changed data to Star Wars sample data */ function WhyGraph(graphson, options) { // Set object properties this.graph = graphson; //this.url = url; this.width = 1200; this.height = 1200; this.options = options; this.force = null; this.svg = null; this.link = null; this.label = null; this.labelpaths = null; this.defs = null; this.node = null; this.scale = 15; this.gCharge = -5000; this.gLink = 70; this.linkedByIndex = {}; this.init = function init() { // Initialize the force from options this.force = this.init_force(); // Initialize the svg canvas to draw on this.svg = d3.select("#graph").append("svg") .attr("width", this.width) .attr("height", this.height); // Initialize the link function this.link = this.svg.selectAll(".link") .data(this.graph.links) .enter().append("line") .attr("class", "link"); // Initialize the path for the labels this.labelpaths = svg.selectAll(".edgepath") .data(this.graph.links) .enter() .append('path') .attr({'d': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y}, 'class':'edgepath', 'fill-opacity':0, 'stroke-opacity':0, 'fill':'blue', 'stroke':'red', 'id':function(d,i) {return 'edgepath'+i}}) .style("pointer-events", "none"); // Initialize the labels this.label = this.svg.selectAll(".edgelabel") .data(this.graph.links) .enter().append("text") .style("pointer-events", "none") .attr({'class':'edgelabel', 'id':function(d,i){return 'edgelabel'+i}, 'dx':80, 'dy':0, 'font-size':10, 'fill':'#aaa'}); this.label.append("textPath") .attr('xlink:href',function(d,i) {return '#edgepath'+i}) .style("pointer-events", "none") .text(function(d,i){return d.label;}); // Fill linkedByIndex with all da links graph.links.forEach(function(d) { linkedByIndex[d.source.index + "," + d.target.index] = 1; }); console.log(this.link); // Initialize the defs function this.defs = this.svg.append("svg:defs"); this.defs.append("svg:clipPath") .attr("id", "clipper") .append("svg:circle") .attr("r", scale); // Initialize the node function this.node = svg.selectAll(".node") .data(this.graph.nodes) .enter().append("g") .attr("class", "node") .call(this.force.drag) .on("mouseover", this.mouse_change(.3)) .on("mouseout", this.mouse_change(1)); this.node.append("circle") .attr("class", "node") .attr("r", scale + 1) .attr("opacity", .8) .style("fill", function(d) { return d3.rgb(0,0,0); }) .style("stroke", "black") .style("stroke-width", 0); this.node.append("image") .attr("clip-path", "url(#clipper)") .attr("xlink:href", function(d) {return d.img;}) .attr("x", -scale) .attr("y", -scale) .attr("width", scale * 2) .attr("height", scale * 2) .on("mouseover", this.node_mouseover) .on("mouseout", this.node_mouseout); this.node.append("text") .attr("class", "text") .attr("dx", function(d) { return -12; }) .attr("dy", function(d) { return 25; }) .text(function(d) { return d.name; }); this.node.append("title") .text(function(d) { return d.name; }); this.force.on("tick", this.force_tick(this.link, this.node)); }; this.init_force = function init_force() { var force = d3.layout.force() .charge(this.gCharge) .linkDistance(this.gLink) .size([this.width, this.height]); force.nodes(this.graph.nodes) .links(this.graph.links) .start(); return force }; this.node_mouseover = function node_mouseover(d) { d3.select(this) .transition() .attr("transform", "scale(2.0)"); d3.select(this.parentNode) .select("circle") .transition() .attr("r", scale * 2 + 4); d3.select(this.parentNode) .select("text") .transition() .attr("class","text-hover") .attr("dy", scale * 3); //debugger; link.attr("class",function(e) {return e.source.index == d.index || e.target.index == d.index ? "link-hover" : "link";}); }; this.node_mouseout = function node_mouseout(d) { d3.select(this) .transition() .attr("transform", "scale(1.0)"); d3.select(this.parentNode) .select("circle") .transition() .attr("r", scale + 1); d3.select(this.parentNode) .select("text") .transition() .attr("class","text") .attr("dy", scale * 2); link.attr("class",function(e) {return "link";}); }; this.is_connected = function is_connected(a, b) { return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index; } this.mouse_change = function mouse_change(opacity) { return function(d) { node.style("stroke-opacity", function(o) { thisOpacity = is_connected(d, o) ? 1 : opacity; this.setAttribute('fill-opacity', thisOpacity); d3.select(this).select("image").attr("opacity", thisOpacity); return thisOpacity; }); link.style("stroke-opacity", function(o) { return o.source == d || o.target == d ? 1 : opacity; }); }; } this.force_tick = function force_tick(link, node) { return function() { link.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; }); node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); labelpaths.attr('d', function(d) { var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y; //console.log(d) return path}); label.attr("transform",function(d) { if (d.target.x