Forked from Micak Stubb’s Bl.ock, which was forked originally from Matteo Abrate’s Circle packing with hierarchical edge bundling
Changes by Curran:
attrs
, so it’s just vanilla D3 now!An iteration on the previous example, this time leveraging hierarchical edge bundling to show imports between packages in the historic flare Actionscript visualization library. Direction is ignored.
this iteration on Circle packing with hierarchical edge bundling from @matteoabrate runs lebab.sh
on the output of the previous decaffeinated iteration. then, some syntax is manually optimized for readability.
this iteration makes the code (subjectively) nice to work with
use decaffeinate to produce modern Javascript
that modern Javascript is then Prettier formatted https://prettier.io/
special thanks to @currankelleher who tweeted about this nice block and motivated me to fork it.
const svg = d3.select('svg')
const { width } = svg.node().getBoundingClientRect()
const { height } = svg.node().getBoundingClientRect()
//
// ZOOM
//
const zoomable_layer = svg.append('g')
const zoom = d3
.zoom()
.scaleExtent([-Infinity, Infinity])
.on('zoom', () =>
zoomable_layer.attr('transform', d3.event.transform)
)
svg.call(zoom)
const vis = zoomable_layer.append('g')
.attr('transform', `translate(${width / 2},${height / 2})`)
//
// HIERARCHY
//
const stratify = d3
.stratify()
.parentId(d => d.id.substring(0, d.id.lastIndexOf('.')))
//
// PACK
//
const w = width - 8
const h = height - 8
const pack = d3
.pack()
.size([w, h])
.padding(3)
//
// LINE
//
const line = d3
.line()
.curve(d3.curveBundle.beta(1))
.x(d => d.x)
.y(d => d.y)
const bubble_layer = vis.append('g').attr('transform', `translate(${-w / 2},${-h / 2})`)
d3.csv('flare.csv').then(data =>
d3.csv('flare_links.csv').then(links_data => {
//
// LAYOUT
//
// circle packing
const root = stratify(data)
.sum(d => d.value)
.sort((a, b) => d3.descending(a.value, b.value))
pack(root)
//
// BUNDLING
//
// index nodes & objectify links
const index = {}
root.eachBefore(d => (index[d.data.id] = d))
links_data.forEach(d => {
d.source = index[d.source]
d.target = index[d.target]
return (d.path = d.source.path(d.target))
})
// bubbles
const bubbles = bubble_layer.selectAll('.bubble').data(root.descendants())
const enb = bubbles
.enter()
.append('circle')
.attr('class', 'bubble')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', d => d.r)
enb.append('title').text(d =>
d.id
.substring(d.id.lastIndexOf('.') + 1)
.split(/(?=[A-Z][^A-Z])/g)
.join(' ')
)
// links
const links = bubble_layer.selectAll('.link').data(links_data)
return links
.enter()
.append('path')
.attr('class', 'link')
.attr('d', d => line(d.path))
})
)
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Circle Packing with hierarchical edge bundling</title>
<link type='text/css' href='index.css' rel='stylesheet'/>
<script src='https://d3js.org/d3.v5.min.js'></script>
</head>
<body>
<svg></svg>
<script src='index.js'></script>
</body>
</html>
body, html {
padding: 0;
margin: 0;
height: 100%;
}
svg {
width: 100%;
height: 100%;
background: white;
}
.bubble {
fill: rgba(0,0,0,0.1);
}
.bubble:hover {
stroke: black;
vector-effect: non-scaling-stroke;
}
.link {
fill: none;
stroke: rgb(60,120,205);
stroke-width: 0.2;
vector-effect: non-scaling-stroke;
pointer-events: none;
}
# safe
lebab --replace index.js --transform arrow
lebab --replace index.js --transform for-of
lebab --replace index.js --transform for-each
lebab --replace index.js --transform arg-rest
lebab --replace index.js --transform arg-spread
lebab --replace index.js --transform obj-method
lebab --replace index.js --transform obj-shorthand
lebab --replace index.js --transform multi-var
# unsafe
lebab --replace index.js --transform let
lebab --replace index.js --transform template