Public
Edited
Mar 25, 2024
1 star
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr('width', width)
.attr('style', 'background: rgb(241, 239, 229);')
.attr("viewBox", [-10, -10, width+20, height+20])
.style("font", "10px sans-serif")
.attr("text-anchor", "middle");
const shadow = DOM.uid("shadow");

svg.append("filter")
.attr("id", shadow.id)
.attr("x", "-200%")
.attr("y", "-200%")
.attr("width", "400%")
.attr("height", "400%")
.append("feDropShadow")
.attr("flood-opacity", 1)
.attr("dx", 1.2)
.attr("dy", 1.2);
const zoomable_layer = svg.append('g')
const is_component = (d) => d.depth == 2 || d.parent && d.parent.children.length > 2

const node = zoomable_layer.selectAll("g")
.data(tree.root.descendants())
.join("g")
.attr("transform", d => `translate(${d.xr},${d.yr})`);

const folder = node.filter(d => d.children);
folder.append("circle")
.attr("r", d => d.r)
.attr("fill", d => d.depth <= 1 ? 'none' : color(d.depth-2))
//.attr("filter", d => d.depth == 0 ? '' : shadow)
//.attr("stroke-width", d => d.depth == 2 ? 0.2 : ((d.depth-2) % 10 ? 0.08: 0.15) )
.attr("stroke-width", d => is_component(d) ? 0.3 : 0) //((d.depth-2) % 10 ? 0.08: 0.15) )
.attr("stroke", d => d.depth <= 1 ? 'none' : 'black')
.attr("vector-effect", "non-scaling-stroke");

folder.append("title")
.text(d => d.depth-2)
const leaf = node.filter(d => !d.children);
leaf.append("circle")
.attr("r", d => d.r)
.attr("fill", 'black')/*
.append("rect")
.attr("width", d => d.r*Math.sqrt(2))
.attr("height", d => d.r*Math.sqrt(2))
.attr("x", d => -d.r*Math.sqrt(2)/2)
.attr("y", d => -d.r*Math.sqrt(2)/2)
.attr("fill", 'black')
//.attr("fill-opacity", 0.5);
/*leaf.each(d => d.rand = d.data.size / Math.pow(d.data.order,2))
.append("rect")
.attr("width", d => d.r*Math.sqrt(2))
.attr("height", d => d.r*d.rand*Math.sqrt(2))
.attr("x", d => -d.r*Math.sqrt(2)/2)
.attr("y", d => (1/2 - d.rand)*d.r*Math.sqrt(2))
.attr("fill", 'black');*/
// .attr("id", d => (d.leafUid = DOM.uid("leaf")).id);
/*
leaf.append("clipPath")
.attr("id", d => (d.clipUid = DOM.uid("clip")).id)
.append("use")
.attr("xlink:href", d => d.leafUid.href);
leaf.append("text")
.attr("clip-path", d => d.clipUid)
.selectAll("tspan")
.data(d => d.data.name.split(/(?=[A-Z][^A-Z])/g))
.join("tspan")
.attr("x", 0)
.attr("y", (d, i, nodes) => `${i - nodes.length / 2 + 0.8}em`)
.text(d => d);
*/
/*node.append("title")
.text(d => `${d.ancestors().map(d => d.data.name).reverse().join("/")}\n${format(d.value)}`);
*/
leaf.append("title")
.text(d => d.data.size)

/*const link = zoomable_layer.selectAll("path")
.data(tree.links)
.join("path")
.attr("d", d => `M${d.source.xr} ${d.source.yr} C${d.source.xr} ${d.source.yr-Math.abs(d.target.xr-d.source.xr)*0.5} ${d.target.xr} ${d.target.yr-Math.abs(d.target.xr-d.source.xr)*0.5} ${d.target.xr} ${d.target.yr}`)
.attr("stroke", "rgba(255,255,255,0.1)")
.attr("fill", "none")
.attr("stroke-width", 2)
.attr("vector-effect", "non-scaling-stroke");*/
function zoomed() {
zoomable_layer.attr('transform', d3.event.transform);
}
svg.call(d3.zoom()
.scaleExtent([0, Infinity])
.on('zoom', zoomed));
return svg.node();
}
Insert cell
color = (i) => {
const N = 10
let ii = i % N
let ic = Math.floor(i / N)
const minH = 220+60*4
const H = 60
const Hshift = 75
const C = 15
const Cshift = 10
const L = 92
const Lshift = 16
let ni = Math.pow(ii/(N-1),0.6)
return d3.hcl(minH+ic*H+Hshift*ni, C+Cshift*ni, L-Lshift*ni)
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tree = {
let node = pack(leafified_data)
node.descendants().forEach(d => spiralify(d, spirality))
return {
root: node,
links: node.children[0].children[0].leaves().filter((d,i) => i%10>5).map(n1 => node.children[0].children[0].leaves().filter((d,i) => i%11<5).map(n2 => ({source: n1, target: n2, weight: Math.random()}))).flat()
}
}
Insert cell
spirality = 3.0
Insert cell
function spiralify(node, spirality) {
let ancestorNode = node
node.xr = node.x
node.yr = node.y
while(ancestorNode.parent) {
ancestorNode = ancestorNode.parent
const result = rotate(node.xr, node.yr, ancestorNode.x, ancestorNode.y, spirality*Math.sqrt(ancestorNode.height))
node.xr = result[0]
node.yr = result[1]
}
}
Insert cell
function rotate(x, y, cx, cy, angle) {
var radians = (Math.PI / 180) * angle,
cos = Math.cos(radians),
sin = Math.sin(radians),
nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
return [nx, ny];
}
Insert cell
data = ({
children: [
//await FileAttachment("batagelj_et_al_k_tree@1.json").json(),
//await FileAttachment("wnen30_noun_k_tree@1.json").json(),
//await FileAttachment("wnen30_k_tree.json").json(),
//await FileAttachment("wnen30_w_words_k_tree.json").json(),
//await FileAttachment("wnen30_w_senses_k_tree.json").json(),
//await FileAttachment("barabasi_albert_100k_k_tree@1.json").json(),
//await FileAttachment("barabasi_albert_100k_3_k_tree.json").json(),
//await FileAttachment("erdos_renyi_1k_0.5_k_tree.json").json(),
//await FileAttachment("invite_link_network_k_tree@1.json").json(),
//await FileAttachment("10k_k_tree.json").json(),
//await FileAttachment("100k_k_tree.json").json(),
//await FileAttachment("500k_k_tree.json").json(),
//await FileAttachment("750k_k_tree.json").json(),
//await FileAttachment("1m_k_tree.json").json(),
//await FileAttachment("GrQc.json").json(),
//await FileAttachment("AstroPh.json").json(),
//await FileAttachment("CondMat.json").json(),
//await FileAttachment("HepPh.json").json(),
//await FileAttachment("HepTh.json").json(),
await FileAttachment("corporate_directors.json").json(),
//await FileAttachment("wiki_users.json").json()
]
})
Insert cell
key = 'order'
Insert cell
leafified_data = {
let leafify = (node) => {
var n = {children: []};
if(node.children)
n.children = node.children.map(leafify);
if(node.shell_order > 0)
n.children.push({value: node.shell_order, order: node.shell_order, size: node.shell_size});
return n;
}
return leafify(data);
}
Insert cell
leaves_tree = {
var tree = {children: []}
var walk = (n) => {
if(!n.children)
tree.children.push(n)
else
n.children.map(walk)
}
walk(leafified_data)
return tree
}
Insert cell
pack = data => d3.pack()
.size([width - 2, height - 2])
.padding((d) => d.depth == 0 ? 60 : 6)
//.padding(6)
//.padding((d) => d.height == 1 ? 2 : 6)
(d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value))
Insert cell
height = width
Insert cell
format = d3.format(",d")
Insert cell
d3 = require("d3@5")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more