|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
body { |
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
|
margin: 0; |
|
} |
|
|
|
.links { |
|
fill: none; |
|
stroke: #000; |
|
} |
|
|
|
.link-extensions { |
|
fill: none; |
|
stroke: #000; |
|
stroke-opacity: .25; |
|
} |
|
|
|
.labels { |
|
font: 10px sans-serif; |
|
font-weight: bold; |
|
} |
|
|
|
|
|
</style> |
|
<label id="show-length"> |
|
</label> |
|
|
|
<!-- Copyright 2011 Jason Davies https://github.com/jasondavies/newick.js --> |
|
<script>function parseNewick(a){for(var e=[],r={},s=a.split(/\s*(;|\(|\)|,|:)\s*/),t=0;t<s.length;t++){var n=s[t];switch(n){case"(":var c={};r.branchset=[c],e.push(r),r=c;break;case",":var c={};e[e.length-1].branchset.push(c),r=c;break;case")":r=e.pop();break;case":":break;default:var h=s[t-1];")"==h||"("==h||","==h?r.name=n:":"==h&&(r.length=parseFloat(n))}}return r}</script> |
|
|
|
<!-- Copyright 2016 Mike Bostock https://d3js.org --> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="https://unpkg.com/[email protected]/build/d3-jetpack.js"></script> |
|
|
|
<script> |
|
const outerRadius = 900/2, |
|
innerRadius = outerRadius - 150; |
|
|
|
const color = d3 |
|
.scaleOrdinal() |
|
.domain(["1.", "2.", "3.", "4.", "5.", "6.", "7."]) |
|
.range(d3.schemeCategory10); |
|
|
|
const cluster = d3 |
|
.cluster() |
|
.size([360, innerRadius]) |
|
.separation(function(a, b) { |
|
return 1; |
|
}); |
|
|
|
const svg = d3 |
|
.select('body') |
|
.append('svg') |
|
.at({ |
|
width: outerRadius * 2, |
|
height: outerRadius * 2, |
|
}); |
|
|
|
const chart = svg.append('g').translate([outerRadius, outerRadius]); |
|
|
|
d3.text( |
|
'https://gist.githubusercontent.com/basilesimon/fa37a436836e3556b1cc36a5067e5e33/raw/a4dddacc9e4332219ac5b94568818e7272977cdd/tlp.txt', |
|
function(error, life) { |
|
if (error) throw error; |
|
|
|
const root = d3 |
|
.hierarchy(parseNewick(life), d => d.branchset) |
|
.sum(d => (d.branchset ? 0 : 1)) |
|
.sort( |
|
(a, b) => |
|
a.value - b.value || d3.ascending(a.data.length, b.data.length) |
|
); |
|
|
|
cluster(root); |
|
|
|
setRadius(root, (root.data.length = 0), innerRadius / maxLength(root)); |
|
setColor(root); |
|
|
|
const linkExtension = chart |
|
.append('g') |
|
.attr('class', 'link-extensions') |
|
.selectAll('path') |
|
.data(root.links().filter(d => !d.target.children)) |
|
.enter() |
|
.append('path') |
|
.each(function(d) { |
|
d.target.linkExtensionNode = this; |
|
}) |
|
.attr('d', linkExtensionConstiable); |
|
|
|
const link = chart |
|
.append('g') |
|
.attr('class', 'links') |
|
.selectAll('path') |
|
.data(root.links()) |
|
.enter() |
|
.append('path') |
|
.each(function(d) { |
|
d.target.linkNode = this; |
|
}) |
|
.attr('d', linkConstant) |
|
.attr('stroke', d => d.target.color); |
|
|
|
chart |
|
.append('g') |
|
.attr('class', 'labels') |
|
.selectAll('text') |
|
.data(root.leaves()) |
|
.enter() |
|
.append('text') |
|
.attr('dy', '.31em') |
|
.attr( |
|
'transform', |
|
d => |
|
'rotate(' + |
|
(d.x - 90) + |
|
')translate(' + |
|
(innerRadius + 4) + |
|
',0)' + |
|
(d.x < 180 ? '' : 'rotate(180)') |
|
) |
|
.attr('text-anchor', d => (d.x < 180 ? 'start' : 'end')) |
|
.text(d => (d.data.name.length < 3 ? d.data.name : '')); |
|
|
|
|
|
} |
|
); |
|
|
|
// Compute the maximum cumulative length of any node in the tree. |
|
const maxLength = d => |
|
d.data.length + (d.children ? d3.max(d.children, maxLength) : 0); |
|
|
|
// Set the radius of each node by recursively summing and scaling the distance from the root. |
|
const setRadius = (d, y0, k) => { |
|
d.radius = (y0 += d.data.length) * k; |
|
if (d.children) |
|
d.children.forEach(function(d) { |
|
setRadius(d, y0, k); |
|
}); |
|
}; |
|
|
|
// Set the color of each node by recursively inheriting. |
|
const setColor = d => { |
|
const name = d.data.name.substring(0,2); |
|
d.color = |
|
color.domain().indexOf(name) >= 0 |
|
? color(name) |
|
: d.parent ? d.parent.color : null; |
|
if (d.children) d.children.forEach(setColor); |
|
}; |
|
|
|
const linkConstiable = d => |
|
linkStep(d.source.x, d.source.radius, d.target.x, d.target.radius); |
|
|
|
const linkConstant = d => |
|
linkStep(d.source.x, d.source.y, d.target.x, d.target.y); |
|
|
|
const linkExtensionConstiable = d => |
|
linkStep(d.target.x, d.target.radius, d.target.x, innerRadius); |
|
|
|
const linkExtensionConstant = d => |
|
linkStep(d.target.x, d.target.y, d.target.x, innerRadius); |
|
|
|
// Like d3.svg.diagonal.radial, but with square corners. |
|
function linkStep(startAngle, startRadius, endAngle, endRadius) { |
|
const c0 = Math.cos((startAngle = (startAngle - 90) / 180 * Math.PI)), |
|
s0 = Math.sin(startAngle), |
|
c1 = Math.cos((endAngle = (endAngle - 90) / 180 * Math.PI)), |
|
s1 = Math.sin(endAngle); |
|
return ( |
|
'M' + |
|
startRadius * c0 + |
|
',' + |
|
startRadius * s0 + |
|
(endAngle === startAngle |
|
? '' |
|
: 'A' + |
|
startRadius + |
|
',' + |
|
startRadius + |
|
' 0 0 ' + |
|
(endAngle > startAngle ? 1 : 0) + |
|
' ' + |
|
startRadius * c1 + |
|
',' + |
|
startRadius * s1) + |
|
'L' + |
|
endRadius * c1 + |
|
',' + |
|
endRadius * s1 |
|
); |
|
} |
|
|
|
|
|
</script> |