{
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
const color = d3.scaleOrdinal(d3.schemeCategory10);
const distance = width / 20,
r = distance / 5,
a = {id: 0, value: r, fx: - width / 3, fy: 0, rpa: false},
b = {id: 1, value: 2 * r, fy: 0, rpa: false},
c = {id: 2, value: 4 * r, fy: 0, rpa: false},
d = {id: 3, value: 3 * r, fy: 0, rpa: false},
e = {id: 4, value: 2.5 * r, fy: 0, rpa: false},
f = {id: 5, value: 1.5 * r, fx: width / 3, fy: 0, rpa: false},
nodes = [a, b, c, d, e, f],
links = [ { source: a, target: b},
{ source: b, target: c},
{ source: c, target: d},
{ source: d, target: e},
{ source: e, target: f}
];
var simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(- width))
.force("link", d3.forceLink(links).distance(distance))
.force("x", d3.forceX())
.force("y", d3.forceY())
.on("tick", ticked);
var g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"),
link = g.append("g").attr("stroke", "#000").attr("stroke-width", 1.5).selectAll(".link"),
node = g.append("g").attr("stroke", "#fff").attr("stroke-width", 1.5).selectAll(".node");
restart();
d3.select(button).on("click", addNode);
function addNode() {
const nonRpa = nodes.filter(d => !d.rpa);
const biggest = d3.max(nonRpa, d => d.value);
const smallest = d3.min(nonRpa, d => d.value);
const nextId = d3.max(nodes, d => d.id) + 1;
const source = nodes.filter(d => (!d.rpa) && d.value == smallest)[0];
const target = nodes.filter(d => (d.value == biggest))[0];
// Reset node value
nodes.forEach(d => d.value += d.rpa ? 0 : biggest / nodes.length);
target.rpa = true;
target.value = 10;
nodes.push({ id: nextId, value: d3.max([10, Math.random() * 30]), rpa: true });
links.push({ source: nodes[nodes.length - 1], target: target });
restart();
}
function restart() {
// Apply the general update pattern to the nodes.
node = node.data(nodes, function(d) { return d.id;});
node.exit().remove();
node = node.enter().append("circle").attr("fill", function(d) { return color(d.id); }).merge(node)
node.transition().duration(2000).attr("r", d => d.value);
node.append("title").html(d => d.id);
// Apply the general update pattern to the links.
link = link.data(links, function(d) { return d.source.id + "-" + d.target.id; });
link.exit().remove();
link = link.enter().append("line").merge(link);
// Update and restart the simulation.
simulation.nodes(nodes);
simulation.force("link").links(links);
simulation.alpha(1).restart();
}
function ticked() {
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
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; });
}
invalidation.then(() => simulation.stop());
return svg.node();
}