{
const enterColor = "#c6db5f",
updateColor = "#759e7e",
svgBackgroundColor = '#152c33',
radius = 10,
fontSize = ".7em",
svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.style("background-color", svgBackgroundColor),
container = svg.append("g")
.attr("transform", `translate(20,0)`),
xPosition = .6 * width;
let numNodes = height/(2 * radius);
let y = d3.scaleBand()
.range([(2*radius),(numNodes * (2 * radius))-(radius)])
.domain(d3.range(numNodes))
.padding(0.01);
let arcLinks = [];
let arcNodes = d3.range(numNodes).map(function(i) {
return {
id: i,
radius: radius,
};
});
//creating the links
let count = 0;
for(let n in arcNodes){
for(let n2 in arcNodes){
let s = arcNodes[n].id;
let t = arcNodes[n2].id;
if(s == t || s > t){
//pass
}else{
let link = {id: count, source: s, target: t};
arcLinks.push(link);
count++;
}
}
}
function getArc(d){
let sweep = d.source > d.target ? 1 : 0,
start = y(d.source), // y position of start node on the y axis
end = y(d.target) // y position of end node
return ['M', xPosition,start, //starting x,y
'A',
(end-start)/2, //rx - x-axis radius
',',
(end-start)/2, //ry - y-axis radius
0, //x-axis rotation
0, //large-arc-flag
',',
sweep, //sweep flag
xPosition, //ending x
',',
end //ending y
]
.join(' ');
}
function update(nodes,links){
let t = d3.transition()
.duration(1550);
let u = d3.transition()
.duration(1950);
let removeTransition = d3.transition().duration(750);
let circles = container.selectAll("circle")
.data(nodes,d=>d.id);
circles
.join(
enter => enter.append("circle")
.style("opacity", 0)
.attr('cy', d => y(d.id))
.attr("fill", enterColor)
.call(e => e.transition(t)
.attr('r', d => d.radius)
.style('opacity', 1)
),
update => update
.call(e => e.transition(t)
.attr('cy', d => y(d.id))
.attr("fill", updateColor)
.style('opacity', 1)
),
exit => exit
.style("opacity", 0.2)
.call(e => e.transition(removeTransition)
.remove()
)
)
//added the radius because you can see the lines under the circles sometimes
.attr('cx', xPosition + radius);
let circleText = container.selectAll("text")
.data(nodes,d=>d.id);
circleText
.join(
enter => enter.append("text")
.attr("opacity", 0)
.attr('y', d => y(d.id)+4)
.attr("fill", updateColor)
.attr("font-size", fontSize)
.attr("font-weight", "bold")
.attr("font-family", "helvetica")
.call(e => e.transition(t)
.attr('opacity', 1)
.text(d=>d.id)
),
update => update
.call(e => e.transition(t)
.attr("fill", enterColor)
.attr('y', d => y(d.id)+4)
.text(d=>d.id)
.style('opacity', 1)
),
exit => exit
.style("opacity", 0.2)
.call(e => e.transition(removeTransition)
.remove()
)
)
.attr('x', xPosition+radius)
.attr("text-anchor", "middle")
let arcLinksPaths = container.selectAll('path')
.data(links, d=>d.id);
arcLinksPaths
.join(
enter => enter.append("path")
.style("opacity", 0)
.attr("stroke", enterColor)
.call(e => e.transition(u)
.attr('d', getArc)
.style('opacity', .7)
),
update => update
.style("opacity", 0)
.call(e => e.transition(t)
.attr("stroke", updateColor)
.attr('d', getArc)
.style('opacity', .7)
),
exit => exit
.style("opacity", 0.1)
.call(e => e.transition(removeTransition)
.remove()
)
)
.style("fill", "none")
.attr("stroke-width", 1);
}
yield svg.node();
update(arcNodes,arcLinks);
d3.interval(function() {
let newNodes = d3.shuffle(arcNodes)
.slice(0, Math.floor(Math.random() * (arcNodes.length - 5) + 5));
let nodeIds = newNodes.map(d => d.id);
nodeIds.sort(function(a, b) {
return a - b;
});
let nodeSet = new Set(nodeIds);
numNodes = newNodes.length;
let diagramHeight = (2*radius) * numNodes;
//update y scale
y.domain(nodeIds)
.range([((height/2) - (diagramHeight/2)),((height/2) + (diagramHeight/2))]);
let newLinks = [];
for(let i in arcLinks){
let s = arcLinks[i].source;
let t = arcLinks[i].target;
if(nodeSet.has(s) && nodeSet.has(t)){
newLinks.push(arcLinks[i]);
}
}
update(newNodes,newLinks);
}, 1900);
}