Run D3 in a IJavascript notebook

by Philippe Rivière, based on https://gist.github.com/mef/7044786 and https://gist.github.com/danioyuan/d776a8034b64ceaa80bb

We use jsdom to provide a playground for D3’s select().enter().append() pattern.

In [1]:
var css = `
.graph {
  stroke: #f40024;
  stroke-opacity: 1;
  stroke-width: 2;
}
.polygons {
  fill: none;
  stroke: #dbdbdb;
  stroke-width: 1;
}
.sites {
  fill: #000;
  stroke: #fff;
}
`;

var d3 = require('d3')
  , jsdom = require('jsdom')
  , run = (main) => {
    jsdom.env({
      html: `<html><style>${css}</style><body></body></html>`,
      done: function(err, window) {
        // pre-renders the dataviz inside the jsdom window
        document = window.document;
        main (d3.select(document.querySelector('body')));
        // then exports the result to the notebook
        $$.html(d3.select('html').node().innerHTML);
     }
   });
  };
Out[1]:
undefined
In [2]:
function show_urquart(sel) {
    var svg = sel
        .append('svg')
    .attr('width', width)
    .attr('height', height);

    var sites = d3.range(nsites)
    .map(function(d) { return [Math.random() * width, Math.random() * height]; })
    .map(function(d,i) { d.index = i; return d; });

var voronoi = d3.voronoi();

var polygon = svg.append("g")
    .attr("class", "polygons")
  .selectAll("path")
  .data(voronoi.polygons(sites))
  .enter().append("path")
    .call(drawPolygon);

var graph = svg.append('g')
.attr('class', 'graph');

var site = svg.append('g')
.attr('class', 'site')
.selectAll('circle')
.data(sites)
.enter()
.append('circle')
.attr('r',2)
.attr('fill','white')
.attr('stroke','black')
.call(drawSite)
;

var diagram = voronoi(sites);
  
  diagram.urquhart = function(){
    var urquhart = d3.map();
    
    diagram.links()
    .forEach(function(link) {
      var v = d3.extent([link.source.index, link.target.index]);
      urquhart.set(v, link);
    });
    urquhart._remove = [];
    diagram.triangles()
    .forEach(function(t) {
      var l = 0, length = 0, i, v;
      for (var j=0; j<3; j++) {
        var a = t[j], b = t[(j+1)%3];
        v = d3.extent([a.index, b.index]);
        length = (a[0]-b[0]) * (a[0]-b[0]) + (a[1]-b[1]) * (a[1]-b[1]);
        if (length > l) {
          l = length;
          i = v;
        }
      }
      urquhart._remove.push(i);
    });
    urquhart._remove.forEach(function(i) {
      if (urquhart.has(i)) urquhart.remove(i);
    });
    return urquhart.values();
  }

  drawgraph();
  
function drawgraph(){
   var links = diagram.urquhart();
  graph
    .selectAll('line')
   .data(links)
  .enter().append('line')
    .attr('x1', function(l){ return l.source[0];})
    .attr('y1', function(l){ return l.source[1];})
    .attr('x2', function(l){ return l.target[0];})
    .attr('y2', function(l){ return l.target[1];})
}
  
function drawPolygon(polygon) {
  polygon.attr("d", function(d) {
    if (d && !d.filter(function(n){ return !n}).length) {
      return "M" + d.join("L") + "Z";
    }
  });
}
function drawSite(site) {
  site
      .attr("transform", function(d) { return 'translate(' + d + ')'; });
}


} // main
Out[2]:
undefined
In [3]:
var width = 400,
    height = 250,
    nsites = 100;
run(show_urquart)
Out[3]:
undefined
Out[3]:
In [ ]: