Skip to content

Instantly share code, notes, and snippets.

@makyo
Last active March 2, 2016 05:31
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save makyo/6754267 to your computer and use it in GitHub Desktop.
Save makyo/6754267 to your computer and use it in GitHub Desktop.
Mapping poly relationships with force directed layouts

I freely admit that this is outrageously geeky. A conversation with folks on twitter led to attempts to try and describe, visually, polyamorous relationships. We kept finding ourselves annotating the links between us, notably with each end of the link, and adding other types of relationships, not all formal or even romantic. This is simply the standard FDL example, with info added about relationship meaning when you hover over the relationship link itself.

{
"nodes":
[
{"name": "Makyo"},
{"name": "JD"},
{"name": "Forneus"},
{"name": "Des"},
{"name": "Peri"},
{"name": "SQUISH ZONE",
"attrs": {"x": -20, "y": -20, "r": 20, "fill": "#888"}},
{"name": "Loomy"},
{"name": "Jake"},
{"name": "Stripes"},
{"name": "Bellua"},
{"name": "Cullen"},
{"name": "Nakita"},
{"name": "Osric"},
{"name": "Bel"},
{"name": "Lunostophiles"},
{"name": "Floe"}
],
"links":
[
{"source": 0, "target": 1, "value": 1, "targetMeaning": "husbandog",
"sourceMeaning": "husbandfox", "linkMeaning": "partners, civil union"},
{"source": 0, "target": 2, "value": 2, "targetMeaning": "catfriend",
"sourceMeaning": "foxfriend", "linkMeaning": "partners"},
{"source": 2, "target": 3, "value": 1, "targetMeaning": "catfriend",
"sourceMeaning": "catfriend", "linkMeaning": "partners"},
{"source": 2, "target": 4, "value": 3, "dashed": true,
"linkMeaning": "gender, maybe something"},
{"source": 0, "target": 4, "value": 3, "dashed": true,
"linkMeaning": "gender, anxiety, programming"},
{"source": 0, "target": 5, "value": 4,
"targetMeaning": "For all those whom I love",
"sourceMeaning": "Who knows, really."},
{"source": 1, "target": 6, "value": 4, "dashed": true,
"linkMeaning": "Sexuality"},
{"source": 1, "target": 7, "value": 4, "dashed": true,
"linkMeaning": "Guns, etc."},
{"source": 7, "target": 8, "value": 1, "linkMeaning": "partners"},
{"source": 4, "target": 9, "value": 1, "linkMeaning": "partners"},
{"source": 0, "target": 10, "value": 3,
"linkMeaning": "too alike and too far apart"},
{"source": 0, "target": 11, "value": 4, "dashed": true,
"linkMeaning": "roommates, sexually open, close"},
{"source": 1, "target": 11, "value": 4, "dashed": true,
"linkMeaning": "roommates, sexually open, close"},
{"source": 0, "target": 12, "value": 4, "dashed": true,
"linkMeaning": "sexually open, close"},
{"source": 1, "target": 12, "value": 4, "dashed": true,
"linkMeaning": "some other universe, perhaps"},
{"source": 0, "target": 13, "value": 4, "dashed": true,
"linkMeaning": "some other universe, perhaps"},
{"source": 1, "target": 13, "value": 4, "dashed": true,
"linkMeaning": "sexually open, close"},
{"source": 12, "target": 13, "value": 1,
"sourceMeaning": "husband", "targetMeaning": "husband",
"linkMeaning": "partners, civil union"},
{"source": 0, "target": 14, "value": 4, "dashed": true,
"linkMeaning": "Makers-in-common"},
{"source": 2, "target": 14, "value": 4, "dashed": true,
"linkMeaning": "Makers-in-common"},
{"source": 4, "target": 14, "value": 4, "dashed": true,
"linkMeaning": "History"},
{"source": 0, "target": 15, "value": 4, "dashed": true,
"linkMeaning": "Anxiety, earnestness"}
]
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link line {
stroke: rgba(0,0,0,0.35);
}
.link .meaning {
fill: #00F;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
.hidden {
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.gravity(.05)
.distance(100)
.charge(-100)
.size([width, height]);
d3.json("graph.json", function(error, json) {
force
.nodes(json.nodes)
.links(json.links)
.distance(function(d) { return Math.log(d.value * 3) * 50; })
.start();
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("g")
.attr("class", "link");
link.append('text')
.attr('class', 'sourceMeaning meaning hidden')
.text(function(d) { return d.sourceMeaning });
link.append('text')
.attr('class', 'targetMeaning meaning hidden')
.text(function(d) { return d.targetMeaning });
link.append('text')
.attr('class', 'linkMeaning meaning hidden')
.text(function(d) { return d.linkMeaning || ''; });
link.append('line')
.attr("stroke-width", function(d) {
return 10 / (d.value / 1);
})
.attr("stroke-dasharray", function(d) {
if (d.dashed) {
return "5,5";
}
})
.on('mouseover', function(d) {
d3.select(this.parentNode).selectAll('.meaning')
.classed('hidden', false);
})
.on('mouseout', function(d) {
d3.select(this.parentNode).selectAll('.meaning')
.classed('hidden', true);
});
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr('x', function(d) { return d.attrs ? d.attrs.x : -10; })
.attr('y', function(d) { return d.attrs ? d.attrs.y : -10; })
.attr('r', function(d) { return d.attrs ? d.attrs.r : 10 })
.attr('fill', function(d) { return d.attrs ? d.attrs.fill : '#000'});
node.append("text")
.attr("dx", function(d) { return d.attrs ? d.attrs.r + 2 : 12; })
.attr("dy", ".35em")
.text(function(d) { return d.name });
force.on("tick", function() {
link.select('line')
.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; });
link.select('.sourceMeaning')
.attr('dx', function(d) { return d.source.x + 12})
.attr('dy', function(d) { return d.source.y + 24});
link.select('.targetMeaning')
.attr('dx', function(d) { return d.target.x + 12})
.attr('dy', function(d) { return d.target.y + 24});
link.select('.linkMeaning')
.attr('dx', function(d) {
return (d.source.x + ((d.target.x - d.source.x) / 2));
})
.attr('dy', function(d) {
return (d.source.y + ((d.target.y - d.source.y) / 2));
});
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment