Published
Edited
Jul 13, 2021
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const g = clone(kgSW);
const bendDivSW = 1 / bendySW;

const r = new Renderer(w, h, rType)
.pointSize(32)
.textAlign("middle")
.textBaseline("middle")
.textSize(16);
const toPoint = (n) => [n.x, n.y];
const toLine = (e) => [
[e.source.x, e.source.y],
[e.target.x, e.target.y]
];

// -- Bezier control points:
const flowCurve = (e) => [
[e.source.x, e.source.y],
[
e.target.x - (e.source.y - e.target.y) / bendDivSW,
e.target.y + (e.source.x - e.target.x) / bendDivSW
],
[e.target.x, e.target.y],
[e.target.x, e.target.y]
];

//draws nodes and edges;
//incorporates interaction information: if a node is 'picked' draw it and the edges
//it links to with a difference stroke color
const drawFrame = () => {
r.clear().stroke("black");

g.edges.forEach(
(e) => (picked >= 0 && (e.source == g.nodes[picked] || e.target == g.nodes[picked])) ?
r.strokeWidth(e.w).stroke("rgb(255,0,0)").fill("").bezier(flowCurve(e)) :
r.strokeWidth(e.w).stroke("rgb(0,0,0)").fill("").bezier(flowCurve(e))
);
g.nodes.forEach(
(n) => (picked >= 0 && g.nodes[picked] == n) ?
r.strokeWidth(2).stroke("rgb(255,0,0)").fill("rgb(220,140,95)").points([toPoint(n)]) :
r.strokeWidth(2).stroke("rgb(0,0,0)").fill("rgb(220,140,95)").points([toPoint(n)])
);
r.fill("black").stroke().textSize(8).textAlign("middle");
g.nodes.forEach((n) => r.text(n.name, n.x, n.y));
};

simSW.nodes(g.nodes).on("tick", drawFrame);
simSW.force("fEdge").links(g.edges);

//adding interaction: when mouse is pressed we check if it's inside a node and if so
//record the index of the node in 'picked', picked is then reset (-1) once mouse is released;
//when mouse is moved, if a node was picked we move it to the position of the mouse
let picked = -1;
r.addListener("mousedown", (ev) => {
let p = d3.pointer(ev);
for (let i=0; i<g.nodes.length; i++){
let dist = (p[0]-g.nodes[i].x)*(p[0]-g.nodes[i].x) + (p[1]-g.nodes[i].y)*(p[1]-g.nodes[i].y)
if (dist < 100){
picked = i;
drawFrame();
// simSW.alphaTarget(0.3).restart();
break;
}
}
});
r.addListener("mouseup", (ev) => {
picked = -1;
drawFrame();
// simSW.alphaTarget(0);
});
r.addListener("mousemove", (ev) => {
if (picked >= 0){
g.nodes[picked].x = d3.pointer(ev)[0];
g.nodes[picked].y = d3.pointer(ev)[1];
drawFrame();
}
});
return r.render();
}
Insert cell
Insert cell
Insert cell
kgSW = {
bendySW;
return {
// -- KG entities
nodes: [
{ id: "a", name: "England" },
{ id: "b", name: "DEFRA" },
{ id: "c", name: "Rural Payments Agency" },
{ id: "d", name: "25 Year Environment" },
{ id: "e", name: "Agriculture" },
{ id: "f", name: "Country" },
{ id: "g", name: "Environ" },
{ id: "h", name: "Environ" }
],
// -- KG relationships
edges: [
{ source: "b", target: "a", w: "5" },
{ source: "b", target: "c", w: "3" },
{ source: "b", target: "d", w: "3" },
{ source: "c", target: "f", w: "1" },
{ source: "c", target: "g", w: "1" },
{ source: "c", target: "h", w: "1" },
{ source: "e", target: "d", w: "2" },
{ source: "e", target: "f", w: "1" },
{ source: "e", target: "g", w: "1" },
{ source: "e", target: "h", w: "1" }
],
getId: (node) => node.id
};
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more