|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8"> |
|
<title>Mobile Patent Suits</title> |
|
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.29.1"></script> |
|
<style type="text/css"> |
|
|
|
path.link { |
|
fill: none; |
|
stroke: #666; |
|
stroke-width: 1.5px; |
|
} |
|
|
|
marker#licensing { |
|
fill: green; |
|
} |
|
|
|
path.link.licensing { |
|
stroke: green; |
|
} |
|
|
|
path.link.resolved { |
|
stroke-dasharray: 0,2 1; |
|
} |
|
|
|
circle { |
|
fill: #ccc; |
|
stroke: #333; |
|
stroke-width: 1.5px; |
|
} |
|
|
|
text { |
|
font: 10px sans-serif; |
|
pointer-events: none; |
|
} |
|
|
|
text.shadow { |
|
stroke: #fff; |
|
stroke-width: 3px; |
|
stroke-opacity: .8; |
|
} |
|
|
|
</style> |
|
</head> |
|
<body> |
|
<script type="text/javascript"> |
|
|
|
// 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 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}); |
|
}); |
|
|
|
var w = 960, |
|
h = 500; |
|
|
|
var force = d3.layout.force() |
|
.nodes(d3.values(nodes)) |
|
.links(links) |
|
.size([w, h]) |
|
.linkDistance(120) |
|
.charge(-300) |
|
.on("tick", tick) |
|
.start(); |
|
|
|
var svg = d3.select("body").append("svg:svg") |
|
.attr("width", w) |
|
.attr("height", h); |
|
|
|
// Per-type markers, as they don't inherit styles. |
|
svg.append("svg:defs").selectAll("marker") |
|
.data(["suit", "licensing", "resolved"]) |
|
.enter().append("svg:marker") |
|
.attr("id", String) |
|
.attr("viewBox", "0 -5 10 10") |
|
.attr("refX", 15) |
|
.attr("refY", -1.5) |
|
.attr("markerWidth", 6) |
|
.attr("markerHeight", 6) |
|
.attr("orient", "auto") |
|
.append("svg:path") |
|
.attr("d", "M0,-5L10,0L0,5"); |
|
|
|
var path = svg.append("svg:g").selectAll("path") |
|
.data(force.links()) |
|
.enter().append("svg:path") |
|
.attr("id", function(d) { return d.source.index + "_" + d.target.index; }) |
|
.attr("class", function(d) { return "link " + d.type; }) |
|
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; }); |
|
|
|
var circle = svg.append("svg:g").selectAll("circle") |
|
.data(force.nodes()) |
|
.enter().append("svg:circle") |
|
.attr("r", 6) |
|
.call(force.drag); |
|
|
|
var text = svg.append("svg:g").selectAll("g") |
|
.data(force.nodes()) |
|
.enter().append("svg:g"); |
|
|
|
// A copy of the text with a thick white stroke for legibility. |
|
text.append("svg:text") |
|
.attr("x", 8) |
|
.attr("y", ".31em") |
|
.attr("class", "shadow") |
|
.text(function(d) { return d.name; }); |
|
|
|
text.append("svg:text") |
|
.attr("x", 8) |
|
.attr("y", ".31em") |
|
.text(function(d) { return d.name; }); |
|
|
|
var path_label = svg.append("svg:g").selectAll(".path_label") |
|
.data(force.links()) |
|
.enter().append("svg:text") |
|
.attr("class", "path_label") |
|
.append("svg:textPath") |
|
.attr("startOffset", "50%") |
|
.attr("text-anchor", "middle") |
|
.attr("xlink:href", function(d) { return "#" + d.source.index + "_" + d.target.index; }) |
|
.style("fill", "#000") |
|
.style("font-family", "Arial") |
|
.text(function(d) { return d.type; }); |
|
|
|
// Use elliptical arc path segments to doubly-encode directionality. |
|
function tick() { |
|
path.attr("d", function(d) { |
|
var dx = d.target.x - d.source.x, |
|
dy = d.target.y - d.source.y, |
|
dr = Math.sqrt(dx * dx + dy * dy); |
|
|
|
if (d.source.x < d.target.x) { |
|
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; |
|
} |
|
else { |
|
return "M" + d.target.x + "," + d.target.y + "A" + dr + "," + dr + " 0 0,1 " + d.source.x + "," + d.source.y; |
|
} |
|
}); |
|
|
|
circle.attr("transform", function(d) { |
|
return "translate(" + d.x + "," + d.y + ")"; |
|
}); |
|
|
|
text.attr("transform", function(d) { |
|
return "translate(" + d.x + "," + d.y + ")"; |
|
}); |
|
} |
|
|
|
</script> |
|
</body> |
|
</html> |