block by curran 13d244f9ca07790115f8429a45db63f0

circle pack + hierarchical edge bundling | es2015+ | Vanilla D3v5

Full Screen

Forked from Micak Stubb’s Bl.ock, which was forked originally from Matteo Abrate’s Circle packing with hierarchical edge bundling

Changes by Curran:


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.

index.js

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))
  })
)

index.html

<!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>

flare.csv

index.css

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;
}

lebab.sh

# 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