Public
Edited
Oct 24, 2023
Insert cell
Insert cell
Insert cell
{
let height = 900;

let two = new Two({
type: Two.Types.canvas,
width: width,
height: height
});
let offscreen = new Two({
type: Two.Types.canvas,
width: width,
height: height,
domElement: new OffscreenCanvas(0,0)
});

let visualisation = create(two, offscreen);

two.renderer.domElement.addEventListener('mousemove', (e) => {
onMousemove(e, visualisation, offscreen);
two.update();
});

two.renderer.domElement.addEventListener('click', (e) => {
onMouseClick(e, visualisation, offscreen);
two.update();
});
return two.renderer.domElement;
}
Insert cell
Insert cell
Insert cell
Insert cell
function create(two, offscreen) {
let group = two.makeGroup();
group.translation.set(two.width/2, two.height/2);

let offscreenGroup = offscreen.makeGroup();
offscreenGroup.translation.set(offscreen.width/2, offscreen.height/2);

//
let members = teams.reduce((acc, cur) => {
return acc.concat(cur.members);
},[]);

let connections = makeRandomConnections(members);

let teamConnections = makeConnectionsBetweenTeams(connections);

let xScale = d3.scaleBand()
.domain(members.map( m => m.id))
.range([0, 2 * Math.PI])
.paddingInner(0.5)
.paddingOuter(0.25)
let yScale = d3.scaleOrdinal()
.domain([1,2,3,4])
.range([195, 200, 210, 215]);

teams.forEach( team => {
let first = team.members[0];
let last = team.members[team.members.length-1];
let sa = xScale(first.id) - (Math.PI/2)
let ea = (xScale(last.id) + xScale.bandwidth()) - (Math.PI/2);


// --
let testArc = two.makeArcSegment(0, 0, yScale(3), yScale(4)+150, sa, ea);
testArc.fill = team.color;
testArc.opacity = 0.1;
testArc.stroke = 'none';

let testArcOffscreen = offscreen.makeArcSegment(0, 0, yScale(3), yScale(4)+150, sa, ea);
testArcOffscreen.fill = getRandomRgb();
testArcOffscreen.stroke = 'none';

LOOKUP[testArcOffscreen.fill] = team;

offscreenGroup.add(testArcOffscreen);
group.add(testArc);

let theta = (sa + ((ea - sa) / 2)) + (Math.PI/2);
let r = yScale(4)+160;
let x = r * Math.sin(theta);
let y = -(r * Math.cos(theta));
let text = two.makeText(team.name, x, y);
text.size = 14;

if (x >= 0) {
text.alignment = 'start';
text.rotation = theta - degreesToRadians(90);
} else {
text.alignment = 'end';
text.rotation = theta - degreesToRadians(-90);
}

group.add(text);
// --
let arc = two.makeArcSegment(0, 0, yScale(2), yScale(3), sa, ea);
arc.fill = team.color;
arc.stroke = 'none';
let offscreenArc = two.makeArcSegment(0, 0, yScale(2), yScale(3), sa, ea);
offscreenArc.fill = getRandomRgb();
offscreenArc.stroke = 'none';

LOOKUP[offscreenArc.fill] = team;
team.arc = arc;
team.offscreenFill = offscreenArc.fill;
group.add(arc);
offscreenGroup.add(offscreenArc);
})

//connections between teams
connections.forEach(connection => {
if(connection.source.team === connection.target.team) { return null }
let path = makeLinePath(connection, 3.5, xScale, yScale(1));
path.fill = 'none';
path.stroke = 'grey';
path.opacity = 0.3;
path.linewidth = 1;

connection.path = path;
group.add(path);
});


//connections within teams
connections.forEach(connection => {
if(connection.source.team !== connection.target.team) { return null }
let path = makeLinePath(connection, -6, xScale, yScale(4));
path.fill = 'none';
path.stroke = 'grey';
path.opacity = 0.3;
path.linewidth = 1;

connection.path = path;
group.add(path);
});

members.forEach( (member,i) => {
let theta = xScale(member.id) +(xScale.bandwidth()/2);
let sa = xScale(member.id) - (Math.PI/2)
let ea = (xScale(member.id) + xScale.bandwidth()) - (Math.PI/2);
let arc1 = two.makeArcSegment(0, 0, yScale(1), yScale(2), sa, ea);
let arc2 = two.makeArcSegment(0, 0, yScale(3), yScale(4), sa, ea);

arc1.fill = member.team.color;
arc2.fill = member.team.color;

arc1.stroke = 'none';
arc2.stroke = 'none';
let r2 = yScale(4) + 50;
let x2 = r2 * Math.sin(theta);
let y2 = -(r2 * Math.cos(theta));
let text = two.makeText(member.name,x2, y2);
text.size = 9;
if (x2 >= 0) {
text.alignment = 'start';
text.rotation = theta - degreesToRadians(90);
} else {
text.alignment = 'end';
text.rotation = theta - degreesToRadians(-90);
}

member.arc1 = arc1;
member.arc2 = arc2;
member.text = text;

//

let offscreenArc1 = offscreen.makeArcSegment(0, 0, yScale(0), yScale(1), sa, ea);
let offscreenArc2 = offscreen.makeArcSegment(0, 0, yScale(3), yScale(4), sa, ea);

offscreenArc1.stroke = 'none';
offscreenArc2.stroke = 'none';
offscreenArc1.fill = member.team.offscreenFill;
offscreenArc2.fill = member.team.offscreenFill;
group.add(arc1, arc2, text);
offscreenGroup.add(offscreenArc1, offscreenArc2);
});

//
two.update();
offscreen.update();

return {
teams,
group,
offscreenGroup
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Chance = require('chance');
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