Autotranslation Groovy to JavaScript and D3

Generate a random graph with Groovy, then visualize it with a D3 interactive, force-directed graph.

First we generate the graph (one made of nodes and edges, like a social network graph) and store it in the BeakerX object.

Then we load D3 and set its styles.

Finally, a JavaScript cell gets the data from the BeakerX object and renders it with D3.

This final cell was copied almost verbatim from the D3 documentation. Other D3 examples should be similarly easy to get working in BeakerX.


In [ ]:
def r = new Random()
def nnodes = 100
def nodes = []
def links = []

for (x in (0..nnodes)){
  nodes.add(name:"" + x, group:((int) x*7/nnodes))
}

for (x in (0..(int) nnodes*1.15)) { 
  source = x % nnodes
  target = ((int) log(1 + r.nextInt(nnodes))/log(1.3))
  value = 10.0 / (1 + abs(source - target))
  links.add(source: source, target: target, value: value*value)
}

beakerx.graph = [nodes: nodes, links: links]
OutputCell.HIDDEN

In [ ]:
%%javascript
require.config({
  paths: {
      d3: '//cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min'
  }});

In [ ]:
%%html
<style>
.node {
  stroke: #fff;
  stroke-width: 1.5px;
}

.link {
  stroke: #999;
  stroke-opacity: .6;
}
</style>

In [ ]:
%%javascript

beakerx.displayHTML(this, '<div id="fdg"></div>');

var graph = beakerx.graph

var d3 = require(['d3'], function (d3) {
    
    var width = 600,
        height = 500;

    var color = d3.scaleOrdinal(d3.schemeCategory20);

    var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().distance(30))
        .force("charge", d3.forceManyBody().strength(-200))
        .force("center", d3.forceCenter(width / 2, height / 2))
        .force("y", d3.forceY(width / 2).strength(0.3))
        .force("x", d3.forceX(height / 2).strength(0.3));

    var svg = d3.select("#fdg")
                .append("svg")
                .attr("width", width)
                .attr("height", height)
                .attr("transform", "translate("+[100, 0]+")");

    simulation
          .nodes(graph.nodes)
          .force("link")
          .links(graph.links);

    var link = svg.selectAll(".link")
          .data(graph.links)
          .enter().append("line")
          .attr("class", "link")
          .style("stroke-width", function(d) { return Math.sqrt(d.value); });

    var node = svg.selectAll(".node")
          .data(graph.nodes)
          .enter().append("circle")
          .attr("class", "node")
          .attr("r", 10)
          .style("fill", function(d) { return color(d.group); });

    node.append("title")
          .text(function(d) { return d.name; });

    simulation.on("tick", function() {
        link.attr("x1", function(d) { return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });

        node.attr("cx", function(d) { return d.x; })
            .attr("cy", function(d) { return d.y; });
    });
    
    node.call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended)
    );
    
    function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
    }

    function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }

    function dragended(d) {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
    }
});

In [ ]: