import nbvis.magics
from nbvis.classes import D3, Vis
The %%d3
magic makes your JavaScript code visible to Python.
Code cells beginning with one of these magics and using the --queue
or -q
argument are added to a visualization "queue," which preserves their order. We can immediately call one of these cells by using %%d3
or %%mathbox
without arguments. Code that is run this way is excluded from the queue. To reset the entire queue when re-running a cell, use the --reset
argument.
%%d3
var text = "Hello World!";
element.text(text);
Initialized d3_code container!
Duplicate code is detected.
%%d3 --reset --queue
var x = 2;
Reinitialized d3_code container! Code added to D3 visualization queue ...
%%d3 --queue
var x = 2;
Duplicate code not added to D3 visualization queue ...
We select an SVG container by an identifier svg#slider
that we will specify later on. We also get its width and height, and place its origin at its center.
%%d3 --reset --queue
// select svg container
var svg = d3.select("svg#slider");
// remove elements from SVG
svg.selectAll("*").remove();
// get width, get height
var width = +svg.node().getBoundingClientRect().width;
var height = +svg.attr("height");
// create an svg subcontainer, translate to center
var origin = svg.append("g");
var transform = "translate(" + width/2 + "," + height/2 + ")";
origin.attr("transform", transform);
Reinitialized d3_code container! Code added to D3 visualization queue ...
We add a horizontal axis, which is scaled, to give our slider a metric.
%%d3 --queue
// create linear scale
var xScale = d3.scaleLinear()
.domain([-1, 1])
.range([-width/2 + width/5, width/2 - width/5]);
// create axis, remove ticks
var xAxis = d3.axisBottom(xScale)
.ticks(width/200)
.tickSize(0)
.tickPadding(15);
// style axis
function xAxisStyled(g) {
g.call(xAxis);
// style colour
g.selectAll(".domain")
.styles({
"fill": "none",
"stroke": "#cfcfcf",
"stroke-width": "1"
});
// prevent drag selection of tick labels
g.selectAll(".tick text")
.styles({
"-webkit-user-select": "none",
"-moz-user-select": "none",
"-ms-user-select": "none",
"user-select": "none"
})
}
// add axis to subcontainer
origin.append("g")
.call(xAxisStyled);
Code added to D3 visualization queue ...
A single node is specified, and represented in the SVG element with a circle. We also make this node draggable.
%%d3 --queue
// create node data
var nodes = [{"id": 0}];
// add node to subcontainer, set node attributes
var node = origin.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("r", d => d.radius ? d.radius : 8);
// set drag behaviours
node.call((() => {
function dragstart(d) {
if (!d3.event.active) simulation.alpha(1).restart();
d.fx = d.x;
}
function dragging(d) { d.fx = d3.event.x; }
function dragend(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
}
return d3.drag()
.on("start", dragstart)
.on("drag", dragging)
.on("end", dragend);
})());
Code added to D3 visualization queue ...
We get a slider-like behaviour using forceSimulation()
from d3-force
.
A small, nonnegative argument that is less than one is passed to velocityDecay()
to minimize simulation "friction."
%%d3 --queue
// set force simulation attributes
var simulation = d3.forceSimulation()
.force("x", d3.forceX().x(0))
.alphaDecay(5e-3)
.velocityDecay(1/5);
simulation.nodes(nodes);
Code added to D3 visualization queue ...
A tick
function bounds the movement of the node to its axis, and we keep track of its horizontal position.
Changes to this position are pushed as text and colour to a Markdown cell.
%%d3 --queue
// move node with time, get node position along axis
simulation.on("tick", () => {
node.attr("cx", d => Math.max(-width/2 + width/5 - 8, Math.min(width/2 - width/5 + 8, d.x)))
window.nodeXPosition = node.attr("cx") / (3*width/10);
// select markdown cell
var span = d3.select("#listener")
// interpolate colour scheme from red to white to blue
var interpolator = d3.scaleLinear()
.domain([-1, 0, 1])
.range([
"rgba(255, 0, 0, 0.3)",
"rgba(255, 255, 255, 1)",
"rgba(0, 0, 255, 0.3)"
])
.interpolate(d3.interpolateRgb.gamma(2.2));
// set markdown cell text and color
span.text(window.nodeXPosition.toFixed(2));
span.style('background-color', interpolator(window.nodeXPosition));
});
Duplicate code not added to D3 visualization queue ...
We have defined two classes for handling our code: D3(name)
and MathBox(name)
. Here name
is a string. Each class instance represents a distinct visualization structure, like a plot or a slider. The D3
class has two methods, canvas(height=None)
and svg(height=None)
, for displaying D3 outputs.
When displayed, an instance finds and gathers its dependencies, whereupon it executes the JavaScript queue.
We create a uniquely named D3 object class instance, and append an SVG element. A height
parameter is specified.
slider = D3("slider", silent=False).svg(height=100)
New D3 object "slider" added to instances ...
Finally, we pass a list containing the name of our class instance:
slider.require("d3-selection-multi")
Vis(slider, silent=False);
Will require "d3-selection-multi" ... Found D3 instance of "slider" ... Requiring "d3-selection-multi" ...
Try to drag the circle! This Markdown cell listens for changes to the horizontal position of the slider.
Current slider value: