Published
Edited
Jun 22, 2022
Importers
Insert cell
Insert cell
defaults = ({
RADIUS : 4,
COLLISION_RADIUS : 6,
FORCEMANYBODY_STRENGTH : -100,
XFORCE_STRENGTH : 1,
YFORCE_STRENGTH : 1,
COLLISION_STRENGTH : 1,
ALPHA : 1,
ALPHA_MIN : 0.01,
ALPHA_DECAY : 0.01,
ALPHA_TARGET : 0,
VELOCITY_DECAY : 0.8
})
Insert cell
function randn_bm(min, max, skew) {
var u = 0, v = 0;
while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
while(v === 0) v = Math.random();
let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
num = num / 10.0 + 0.5; // Translate to 0 -> 1
if (num > 1 || num < 0) num = randn_bm(min, max, skew); // resample between 0 and 1 if out of range
num = Math.pow(num, skew); // Skew
num *= max - min; // Stretch to fill range
num += min; // offset to min
return num;
}
Insert cell
function randomIntFromInterval(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}
Insert cell
function getData(numOfNodes, categories, initialPos, size) {
let color = ['#F9AD6E', '#EE6D67', '#F4454F', '#3158D1', '#269574', '#2A7CA1', '#D2C4C2', '#F8A621']
let colorScale = d3.scaleOrdinal()
.domain(categories)
.range(color)
let nodes = []
d3.range(0, numOfNodes).map((d,i) => {
let rand = randomIntFromInterval(1,categories.length)
if(initialPos){
nodes.push({
'band': rand,
'x': initialPos.x,
'y': initialPos.y,
"radius": size,
'color': colorScale(rand),
"id": i
})
} else {
nodes.push({
'band': rand,
"radius": size,
'color': colorScale(rand),
"id": i
})
}
})
return nodes
}
Insert cell
function update(obj, data, type, config) {
let circles = obj.selectAll('circle').data(data, d=>d.id)
circles.exit().remove()
let entered_circles = circles
.enter()
.append('circle')
if(config){
entered_circles
.style('opacity', 1)
.attr('id', d => d.id)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.style('fill', d => d.color)
.attr('r', d => d.radius)
} else {
entered_circles
.style('opacity', 1)
.attr('id', d => d.id)
.style('fill', d => d.color)
.attr('r', d => d.radius)
}
circles = circles.merge(entered_circles)
if(config){
let t = d3.transition()
.duration(config.DURATION)
.ease(d3.easeQuadOut)
if(type=='transition_by_default'){
circles
.transition(t)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', d => d.radius)
.style('fill', d => d.color)
} else if(type=='transition_by_index'){
// transition each node one by one within each group at the same time
config.category.map((d,i)=> {
circles.filter("circle[id^='" + i.toString() + "']")
.transition(t)
.delay(function(d,i){ return config.DELAY*i }) // transition each node one by one based on index
.attr('cx', d => d.x)
.attr('cy', d => d.y)
})
} else if(type=='transition_by_length'){
// transition each node one by one within each group at the same time
config.category.map((d,i)=> {
circles.filter("circle[id^='" + i.toString() + "']")
.transition(t)
.delay(function(d,i){ return d.length*config.DELAY }) // transition each node one by one based on length of trajectory
.attr('cx', d => d.x)
.attr('cy', d => d.y)
})
}
}
return circles
}
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