Last active
December 25, 2015 19:29
-
-
Save jchonglee/7027692 to your computer and use it in GitHub Desktop.
Star Wars graph. Listen to this while viewing: http://www.youtube.com/watch?v=kuovuHvaxlU
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
/* Graph Stylesheet for Why-Visualization */ | |
.node { | |
stroke: #fff; | |
stroke-width: 1.5px; | |
} | |
.text, .text-hover { | |
pointer-events: none; | |
font: 0.3em sans-serif; | |
stroke-width: 1.0px; | |
} | |
.text { | |
opacity: 0.7; | |
stroke: #555; | |
} | |
.text-hover { | |
opacity: 1.0; | |
stroke: #222; | |
} | |
.link { | |
stroke: #888; | |
stroke-opacity: 0.3; | |
stroke-width: 1.0px; | |
} | |
.link-hover { | |
stroke: #888; | |
opacity: .8; | |
stroke-width: 2.0px; | |
} | |
.fade { | |
stroke: #888; | |
opacity: .1; | |
stroke-width: 1.0px; | |
} | |
div#graph { | |
width: 1200px; | |
height: 1200px; | |
margin: auto auto; | |
} |
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> | |
<html> | |
<head> | |
<title>Test Graph</title> | |
<link rel="stylesheet" href="starwhy.css"> | |
</head> | |
<body> | |
<div id="graph"></div> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="starwhy.js"></script> | |
<script> | |
var graph = JSON.parse('{"nodes": [{"group": 5, "type": "thing", "name": "The Force", "img": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSd4aKUMuAb27X2cA3DMwGuvMEt6wr0x9kYusEFLPo02BQ9104swQ"}, {"group": 1, "type": "sith lord", "name": "Darth Vader", "img": "http://images3.wikia.nocookie.net/__cb20130508083822/scifi/images/6/6a/DarthVader083111.jpg"}, {"group": 1, "type": "princess", "name": "Leia Organa", "img": "http://2.bp.blogspot.com/-IsD3IHKT9lk/T23RAe6ms1I/AAAAAAAAE_8/qBG7RIerigs/s1600/princess+leia.jpg"}, {"group": 1, "type": "jedi", "name": "Luke Skywalker", "img": "http://blogs.dailymail.com/nerdliving/files/2011/09/luke-150x150.jpg"}, {"group": 1, "name": "Obi Wan Kenobi", "nickname": "Ben", "type": "jedi", "img": "http://1.bp.blogspot.com/-AMs5cqQwjtg/UTZO22gF2gI/AAAAAAAAATM/M0hgT09XqW4/s1600/alec-guinness-as-ben-obi-wan-kenobi-in-star.jpg"}, {"group": 1, "name": "R2D2", "type": "droid", "specialization": "navigation", "img": "https://si0.twimg.com/profile_images/3303885663/d80bbd27448860d78cc8a6f77236dcf6.jpeg"}, {"group": 1, "name": "C3PO", "type": "droid", "specialization": "human cyborg relations", "img": "http://img.geocaching.com/track/large/c36a8b88-865c-4798-b6a3-36233db56d64.jpg"}, {"group": 1, "type": "smuggler", "name": "Han Solo", "img": "http://img.moviepilot.com/assets/tarantulaV2/project_images_square_big/1360175160_han_solo_movie-oo.jpg"}, {"group": 1, "type": "bounty hunter", "name": "Greedo", "img": "http://images3.wikia.nocookie.net/__cb20111104205227/starwars/images/thumb/c/c6/Greedo.jpg/500px-Greedo.jpg"}, {"group": 1, "type": "crime lord", "name": "Jabba the Hutt", "img": "http://static.giantbomb.com/uploads/square_small/0/7092/202939-jabba.jpg"}, {"group": 1, "type": "smuggler", "name": "Chewbacca", "img": "http://t3.gstatic.com/images?q=tbn:ANd9GcR9hykKAnqvBlyS5L-7_WTvDHXruaUdGrwVbHiA5Fyqz8oy59qqoQ"}, {"group": 1, "type": "pilot", "name": "Biggs Darklighter", "img": "http://2.bp.blogspot.com/-v7FE76fUyD8/TiXFqsEAwMI/AAAAAAAAJSY/xX3WLjXP6kA/s1600/4.jpg"}, {"group": 1, "type": "pilot", "name": "Jek Tono Porkins", "img": "http://www.purplefalcon.com/images/blog/star-wars-whiskey/Jek-Tono-Porkins.jpeg"}, {"group": 1, "type": "pilot", "name": "Wedge Antilles", "img": "http://static.comicvine.com/uploads/square_small/1/15317/491397-wedge_antilles.jpg"}, {"group": 1, "type": "imperial", "name": "Grand Moff Tarkin", "img": "http://images3.wikia.nocookie.net/__cb20081126032741/fanfiction/images/e/eb/Tarkin1.jpg"}, {"group": 4, "type": "star fighter", "name": "TIE Advanced x1", "img": "http://images1.wikia.nocookie.net/__cb20080120220950/starwars/images/6/65/Tiex1-headon.jpg"}, {"group": 4, "type": "freighter", "name": "Millenium Falcon", "img": "http://www.digital-polyphony.com/Celebrity-Image-Star-Wars---Millenium-Falcon-73013.jpg"}, {"group": 4, "type": "star fighter", "name": "X-wing Fighter", "img": "http://icons.iconarchive.com/icons/jonathan-rey/star-wars-vehicles/256/X-Wing-02-icon.png"}, {"group": 2, "type": "group", "name": "Rebel Alliance", "img": "http://store.shadowvexindustries.com/image/cache/data/Stickers/RebelAllianceLogo-500x500.png"}, {"group": 2, "type": "group", "name": "Galactic Empire", "img": "http://fc07.deviantart.net/fs70/f/2010/174/b/8/Galactic_Empire_Symbol_by_William_of_Orange.jpg"}, {"group": 2, "type": "group", "name": "Red Squadron", "img": "http://i26.photobucket.com/albums/c134/Kouroe/red-squadron-logo-2.png"}, {"group": 3, "type": "location", "name": "Death Star", "img": "http://3.bp.blogspot.com/_X3lNYc1hcaE/TJfE_JCNHNI/AAAAAAAAAyc/RzllST9oJVA/s1600/death_star_2.jpg"}, {"group": 3, "type": "planet", "name": "Alderaan", "img": "http://images3.wikia.nocookie.net/__cb20061211013807/starwars/images/4/4a/Alderaan.jpg"}, {"group": 3, "type": "moon", "name": "Yavin IV", "img": "http://static.giantbomb.com/uploads/square_small/0/7941/287645-250px_yavin_4.jpg"}, {"group": 3, "type": "planet", "name": "Tatooine", "img": "http://swc.fs2downloads.com/media/screenshots/Misc/Brand-X/Planets%20-%20Tatooine.jpg"}, {"group": 5, "type": "thing", "name": "Technical Schematics", "img": "http://images2.wikia.nocookie.net/__cb20090224141147/starwars/images/3/3f/Deathstar_blueprint.jpg"}], "links": [{"source": 1, "target": 0, "value": 1, "label": "uses"}, {"source": 3, "target": 0, "value": 1, "label": "uses"}, {"source": 4, "target": 0, "value": 1, "label": "uses"}, {"source": 1, "target": 4, "value": 1, "label": "kills"}, {"source": 2, "target": 1, "value": 1, "label": "father"}, {"source": 3, "target": 1, "value": 1, "label": "father"}, {"source": 1, "target": 15, "value": 1, "label": "flies"}, {"source": 1, "target": 19, "value": 1, "label": "member"}, {"source": 1, "target": 21, "value": 1, "label": "stationed"}, {"source": 4, "target": 1, "value": 1, "label": "trains"}, {"source": 21, "target": 22, "value": 1, "label": "destroys"}, {"source": 14, "target": 21, "value": 1, "label": "commands"}, {"source": 25, "target": 21, "value": 1, "label": "describe"}, {"source": 3, "target": 21, "value": 1, "label": "destroys"}, {"source": 2, "target": 22, "value": 1, "label": "lives"}, {"source": 14, "target": 22, "reason": "to crush rebellion", "value": 1, "label": "harms"}, {"source": 14, "target": 25, "value": 1, "label": "searching"}, {"source": 14, "target": 19, "value": 1, "label": "member"}, {"source": 5, "target": 25, "value": 1, "label": "stores"}, {"source": 2, "target": 25, "value": 1, "label": "smuggling"}, {"source": 3, "target": 25, "method": "accidentally during droid cleaning", "value": 1, "label": "discovers"}, {"source": 2, "target": 3, "value": 1, "label": "brother"}, {"source": 2, "target": 7, "reason": "smoldering chemistry", "value": 1, "label": "hates"}, {"source": 2, "target": 18, "value": 1, "label": "member"}, {"source": 2, "target": 5, "reason": "message in a droid", "value": 1, "label": "uses"}, {"source": 5, "target": 6, "value": 1, "label": "friends"}, {"source": 6, "target": 5, "value": 1, "label": "friends"}, {"source": 3, "target": 2, "value": 1, "label": "sister"}, {"source": 3, "target": 5, "value": 1, "label": "owns"}, {"source": 3, "target": 6, "value": 1, "label": "owns"}, {"source": 3, "target": 18, "value": 1, "label": "member"}, {"source": 3, "target": 20, "value": 1, "label": "member"}, {"source": 3, "target": 11, "value": 1, "label": "friends"}, {"source": 3, "target": 24, "value": 1, "label": "lives"}, {"source": 4, "target": 3, "value": 1, "label": "trains"}, {"source": 4, "target": 18, "value": 1, "label": "member"}, {"source": 4, "target": 24, "value": 1, "label": "lives"}, {"source": 18, "target": 23, "value": 1, "label": "based"}, {"source": 11, "target": 3, "value": 1, "label": "friends"}, {"source": 11, "target": 20, "value": 1, "label": "member"}, {"source": 11, "target": 18, "value": 1, "label": "member"}, {"source": 12, "target": 18, "value": 1, "label": "member"}, {"source": 12, "target": 20, "value": 1, "label": "member"}, {"source": 13, "target": 18, "value": 1, "label": "member"}, {"source": 13, "target": 20, "value": 1, "label": "member"}, {"source": 20, "target": 17, "value": 1, "label": "flies"}, {"source": 7, "target": 2, "reason": "smoldering chemistry", "value": 1, "label": "hates"}, {"source": 7, "target": 15, "value": 1, "label": "damages"}, {"source": 7, "target": 8, "method": "shot first", "value": 1, "label": "kills"}, {"source": 7, "target": 16, "value": 1, "label": "captains"}, {"source": 7, "target": 10, "value": 1, "label": "friends"}, {"source": 9, "target": 7, "label": "wants", "reason": "dumping cargo", "value": 1, "method": "bounty"}, {"source": 9, "target": 24, "value": 1, "label": "lives"}, {"source": 10, "target": 7, "reason": "life debt", "value": 1, "label": "friends"}, {"source": 10, "target": 16, "value": 1, "label": "flies"}]}'); | |
var why = WhyGraph(graph); | |
</script> | |
</body> |
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
/* | |
* why.js | |
* A visualization framework for the D3 of our member-product graphs. | |
* | |
* Author: Jason Chong Lee <jason@cobrain.com> | |
* 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<d.source.x){ | |
bbox = this.getBBox(); | |
rx = bbox.x+bbox.width/2; | |
ry = bbox.y+bbox.height/2; | |
return "rotate(180 "+rx+" "+ry+")"; | |
} | |
else { | |
return "rotate(0)"; | |
} | |
}); | |
} | |
}; | |
this.init(); | |
return this; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment