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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more