Skip to content

Instantly share code, notes, and snippets.

@Andrew-Reid
Last active April 14, 2018 00:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Andrew-Reid/c4160e7617bc5982a81ec91a1c8439d3 to your computer and use it in GitHub Desktop.
Save Andrew-Reid/c4160e7617bc5982a81ec91a1c8439d3 to your computer and use it in GitHub Desktop.
heros and cities 1
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text { font: 12px sans-serif; }
.node--internal text {
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
.link.highlighted {
stroke: #000000;
}
.label {
display:none
}
.node.selected .label {
display:block;
}
</style>
</head>
<body>
<script type="text/javascript">
const data = {"New York":["Spider-Man","Iron Man","Captain America","Hulk","Black Panther","Thor"],"Los Angeles":["Iron Man","Black Widow","Doctor Strange","War Machine","Vision","Scarlet Witch","Hulk","Black Panther","Quicksilver","Ant-Man"],"San Francisco":["Black Widow","Doctor Strange","Hulk","Falcon"],"Washington DC":["Black Widow","Hulk","Falcon"],"Atlanta":["Black Widow"],"Miami":["Doctor Strange","Falcon","Quicksilver"],"San Diego":["Doctor Strange","Hulk","Hawkeye"],"Austin":["Doctor Strange","Captain America"],"Chicago":["War Machine","Hulk","Quicksilver"],"Philadelphia":["Vision","Hulk"],"Houston":["Vision","Star-Lord"],"Phoenix":["Vision"],"Dallas":["Scarlet Witch","Captain America","Drax","Hulk","Quicksilver","Gamora"],"Detroit":["Hulk"],"Seattle":["Hulk","Star-Lord"],"New Orleans":["Black Panther","Thor"],"St. Louis":["Black Panther"],"Indianapolis":["Groot"],"Boston":["Star-Lord"],"Portland":["Rocket Raccoon"],"Pittsburgh":["Rocket Raccoon"]};
const margin = {top: 40, right: 90, bottom: 50, left: 90},
width = 1600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
const svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom),
g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
let cities = Object.keys(data);
let superheros = Array.from(Object.values(data).reduce((acc, heros) => {
heros.forEach(d => acc.add(d));
return acc;
}, new Set()));
cities = _.shuffle(cities);
superheros = _.shuffle(superheros);
const cityNodes = cities.map((x, i, arr) => ({ type: 'city', name: x, x: toScale(i, arr.length), y: 60 }));
const superheroNodes = superheros.map((x, i, arr) => ({ type: 'superhero', name: x, x:toScale(i, arr.length), y: 310 }));
const nodes = cityNodes
.concat(superheroNodes);
const links = cityNodes.reduce((acc, cityNode) => {
return acc.concat(data[cityNode.name].map(hero => ({ source: cityNode, target: superheroNodes.find(x => x.name == hero)})));
}, []);
console.log(links);
console.log(nodes);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.name; }).distance(1))
.force("charge", d3.forceManyBody().distanceMax(cities.length+1/width).strength(10))
.force("center", d3.forceCenter(width / 2, height / 2))
.alphaDecay(0.01);
var link = svg.append("g")
.attr("class", "links")
.selectAll("path")
.data(links)
.enter().append("path")
.attr("class","link")
.attr("d", function (d) {
return diagonal(d.target, d.source);
});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", 5)
.each(function(d) {
(d.type == "city") ? d.fy = 300 : d.fy = 100;
})
.attr("class", function(d) { return (d.type == "city") ? "city":"hero" })
.on('mouseenter', function(d) {
d3.select(this).classed("selected", true)
d3.selectAll('.node').filter(function(otherD) {
const deps = data[d.name] || [];
return deps.includes(otherD.name);
}).classed("selected", true)
.raise();
d3.selectAll('.link').filter(function({source, target}) {
return source.name == d.name || target.name == d.name;
}).classed("highlighted", true);
})
.on('mouseleave', function() {
d3.selectAll('.node').classed("selected", false);
d3.selectAll('.link').classed("highlighted", false);
});
node.append("title")
.text(function(d) { return d.name; });
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(links);
function ticked() {
//*
var force = this;
var alpha = force.alpha();
var padding = 20;
var targetSeparation = (width-padding*2)/(cities.length*2)
if (alpha < 0.5) {
force.force( "collide",d3.forceCollide((0.5 - alpha)*2*targetSeparation) )
.force("charge", d3.forceManyBody().distanceMax(targetSeparation).strength((0.5 - alpha) * 50))
}
node
.attr("cx", function(d) { if (d.x < padding) d.x = padding; else if (d.x > width-padding) d.x = width-padding; return d.x; })
.attr("cy", function(d) { return d.y; });
link
.attr("d", function (d) {
return diagonal(d.target, d.source);
});
}
function toScale(index, length) {
const scale = d3.scaleLinear().domain([0, length - 1]).range([0, width]);
return scale(index);
}
function diagonal(target, source) {
return "M" + target.x + "," + target.y
+ "C" + target.x + "," + (target.y + source.y) / 2
+ " " + source.x + "," + (target.y + source.y) / 2
+ " " + source.x + "," + source.y;
}
//*/
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment