Skip to content

Instantly share code, notes, and snippets.

@erkal
Last active August 29, 2015 13:57
Show Gist options
  • Save erkal/9746628 to your computer and use it in GitHub Desktop.
Save erkal/9746628 to your computer and use it in GitHub Desktop.
Mobile Patent Suits with Polygon Links

Mobile Patent Suits with Polygon Links

This is an alternative to: Mobile Patent Suits

A simpler version is here

How? The width of the links and the length & width of arrow-heads are determined by parameters. These parameters are read from the link properties. Nodes must have a property named "r" which then determines the radius of representing circles. This property is used by the creation of the polygon to shorten the link preventing the arrow head disappearing behind the target node.

Explanation of the Code: Click here to see the picture

Why? svg markers don't seem to be easy to play with, particularly if node radii vary.

What more can be done? One may create curved links by using svg path instead of polygon.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
stroke: white;
stroke-width: 1px;
}
.licensing {
fill: #4DDE00;
}
.suit {
fill: #0772A1;
}
.resolved {
fill: #FF8700;
}
.node {
fill: lightgray;
stroke: black;
stroke-width: 1px;
}
text {
font: 10px sans-serif;
pointer-events: none;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
// http://blog.thomsonreuters.com/index.php/mobile-patent-suits-graphic-of-the-day/
var links = [
{source: "Microsoft", target: "Amazon", type: "licensing"},
{source: "Microsoft", target: "HTC", type: "licensing"},
{source: "Samsung", target: "Apple", type: "suit"},
{source: "Motorola", target: "Apple", type: "suit"},
{source: "Nokia", target: "Apple", type: "resolved"},
{source: "HTC", target: "Apple", type: "suit"},
{source: "Kodak", target: "Apple", type: "suit"},
{source: "Microsoft", target: "Barnes & Noble", type: "suit"},
{source: "Microsoft", target: "Foxconn", type: "suit"},
{source: "Oracle", target: "Google", type: "suit"},
{source: "Apple", target: "HTC", type: "suit"},
{source: "Microsoft", target: "Inventec", type: "suit"},
{source: "Samsung", target: "Kodak", type: "resolved"},
{source: "LG", target: "Kodak", type: "resolved"},
{source: "RIM", target: "Kodak", type: "suit"},
{source: "Sony", target: "LG", type: "suit"},
{source: "Kodak", target: "LG", type: "resolved"},
{source: "Apple", target: "Nokia", type: "resolved"},
{source: "Qualcomm", target: "Nokia", type: "resolved"},
{source: "Apple", target: "Motorola", type: "suit"},
{source: "Microsoft", target: "Motorola", type: "suit"},
{source: "Motorola", target: "Microsoft", type: "suit"},
{source: "Huawei", target: "ZTE", type: "suit"},
{source: "Ericsson", target: "ZTE", type: "suit"},
{source: "Kodak", target: "Samsung", type: "resolved"},
{source: "Apple", target: "Samsung", type: "suit"},
{source: "Kodak", target: "RIM", type: "suit"},
{source: "Nokia", target: "Qualcomm", type: "suit"}
];
var width = 960,
height = 500;
var nodes = {}; // Compute the distinct nodes from the links
links.forEach(function (link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
nodes = d3.values(nodes)
links.forEach(function (link) {
link.linkWidth = 3;
link.headLength = 15;
link.headWidth = 5;
});
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links)
.charge(-3000)
.gravity(.8)
.on("tick", tick)
.start();
// Attach the r attribute looking at the number of neighbors.
// This is necessary for calculatePolygon)
nodes.forEach(function(node) {node.r = 3 + 1.1 * node.weight })
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var svgLinks = svg.selectAll(".link").data(links)
.enter().append("polygon")
.attr("class", function (d) { return "link " + d.type})
var svgNodes = svg.selectAll(".node").data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function (d) {return d.r})
.call(force.drag);
var svgTexts = svg.selectAll("text").data(nodes)
.enter().append("text")
.attr("x", function (d) { return d.r + 6 })
.attr("y", ".31em")
.text(function(d) { return d.name; });
function tick() {
svgNodes.attr("transform", translate);
svgTexts.attr("transform", translate);
svgLinks.attr("points", calculatePolygon);
}
function translate (d) {
return "translate(" + d.x + "," + d.y + ")";
}
function calculatePolygon(d) {
var p2 = d.source,
w = diff(d.target, p2),
wl = length(w),
v1 = scale(w, (wl - d.target.r) / wl),
p1 = sum(p2, v1),
v2 = scale(rotate90(w), d.linkWidth / length(w)),
p3 = sum(p2, v2),
v1l = length(v1),
v3 = scale(v1, (v1l - d.headLength) / v1l),
p4 = sum(p3, v3),
v2l = length(v2),
v4 = scale(v2, d.headWidth / v2l),
p5 = sum(p4, v4);
return pr(p1) +" "+ pr(p2) +" "+ pr(p3) +" "+ pr(p4) +" "+ pr(p5);
function length(v) {return Math.sqrt(v.x * v.x + v.y * v.y)}
function diff(v, w) {return {x: v.x - w.x, y: v.y - w.y}}
function sum(v, w) {return {x: v.x + w.x, y: v.y + w.y}}
function scale(v, f) {return {x: f * v.x, y: f * v.y}}
function rotate90(v) {return {x: v.y, y: -v.x}} // clockwise
function pr(v) {return v.x +","+ v.y}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment