Public
Edited
Feb 16, 2024
Insert cell
md`# Day in the life of...`
Insert cell
renderDayInTheLifeInfographic(NUWANS_ROUTINE, {
title: 'Nuwan I. Senaratna',
subTitle: 'c. 2023'
})
Insert cell
import { addDefaults } from '@nuuuwan/option-utils'
Insert cell
import { range } from '@nuuuwan/list-utils'
Insert cell
import {
DEFAULT_STYLE,
getSVG,
drawCircle,
drawLine,
drawText,
drawDoughnutSlice
} from '@nuuuwan/svg-utils'
Insert cell
import { renderInfographic } from '@nuuuwan/infographic-utils'
Insert cell
EVENT_TYPE_TO_COLOR = Object({
'PRIMARY WORK': '#6eae72',
'OTHER WORK': '#b7dec5',
'SOCIAL & MEALS': '#f6b862',
SLEEP: 'white',
EXERCISE: '#3bc3d7',
'MAKING ENDS MEET': '#939598'
})
Insert cell
NUWANS_ROUTINE = [
Object({
startTime: 5,
eventType: "EXERCISE",
label: "Rise, Meditate"
}),
Object({
startTime: 6,
eventType: "SOCIAL & MEALS",
label: "Black Coffee, Colombo"
}),
Object({
startTime: 6 + 1 / 2,
eventType: "EXERCISE",
label: "Got for a walk in the Park"
}),
Object({
startTime: 8 + 1 / 2,
eventType: "EXERCISE",
label: "Chores & Shower"
}),
Object({
startTime: 9,
eventType: "PRIMARY WORK",
label: "Deep Work 1"
}),
Object({
startTime: 11,
eventType: "PRIMARY WORK",
label: "Deep Work 2"
}),
Object({
startTime: 13,
eventType: "OTHER WORK",
label: "Shallow Work 1"
}),
Object({
startTime: 15,
eventType: "OTHER WORK",
label: "Shallow Work 1"
}),
Object({
startTime: 17,
eventType: "MAKING ENDS MEET",
label: "Shower"
}),
Object({
startTime: 17 + 1 / 4,
eventType: "SOCIAL & MEALS",
label: "Dinner, Documentaries"
}),
Object({
startTime: 18,
eventType: "OTHER WORK",
label: "Reflect - Write Diary, Philosophical Listening"
}),
Object({
startTime: 20,
eventType: "EXERCISE",
label: "Shower, Massage Feet, Yoga Nidra"
}),
Object({ startTime: 21, eventType: "SLEEP", label: "Sleep" })
]
Insert cell
STROKE_COLOR = '#222'
Insert cell
function formatHour(hour) {
hour = hour < 0 ? 24 + hour : hour;
const min = parseInt(hour * 60) % 60;
hour = parseInt(hour);

let sign = 'noon';
if (hour < 12) {
sign = 'am';
} else if (hour === 24) {
sign = 'midnight';
hour = 12;
} else if (hour > 12) {
sign = 'pm';
hour -= 12;
}

if (min === 0) {
return `${hour}${sign}`;
}
return `${hour}.${min}${sign}`;
}
Insert cell
function formatDuration(startHour, endHour) {
if (endHour - startHour <= 1.5) {
return formatHour(startHour);
}
return `${formatHour(startHour)} to ${formatHour(endHour)}`;
}
Insert cell
function renderDayInTheLifeInfographic(eventList, options = {}) {
options = addDefaults(options, {
width: 900,
kicker: 'Day in the life of',
title: 'Add Name',
subTitle: 'Add Dates'
});

options = addDefaults(options, {
height: (options.width * 9) / 16
});

options = addDefaults(options, {
funcDrawInner: function(svg) {
const [cx, cy] = [options.width / 2, options.height / 2];
const r1 = Math.min(cx, cy) * 0.8;
const r2 = r1 * 0.9;
const r3 = r2 * 0.7;

eventList.forEach(function(event, i) {
const endTime = eventList[(i + 1) % eventList.length].startTime;
if (event.startTime > endTime) {
event.startTime -= 24;
}
const theta1 = (2 * Math.PI * event.startTime) / 24;
const theta2 = (2 * Math.PI * endTime) / 24;

drawDoughnutSlice(svg, [cx, cy], r3, r1, theta1, theta2, {
fill: EVENT_TYPE_TO_COLOR[event.eventType],
stroke: 'none'
});

const r4 = r1 * 1.05;
const thetaOriginal = (theta1 + theta2) / 2;
let textAnchor = thetaOriginal < Math.PI ? 'start' : 'end';

const theta = (3 * Math.PI) / 2 - thetaOriginal;
const [x, y] = [cx - r4 * Math.cos(theta), cy + r4 * Math.sin(theta)];

let labelExtended = `${formatDuration(event.startTime, endTime)} ${
event.label
}`;

drawText(svg, [x, y], labelExtended, {
fill: STROKE_COLOR,
textAnchor: textAnchor,
fontFamily: 'Gill Sans',
fontSize: DEFAULT_STYLE.fontSize / 2
});
});

drawCircle(svg, [cx, cy], r1, {
fill: 'None',
stroke: STROKE_COLOR,
strokeWidth: 2
});
drawCircle(svg, [cx, cy], r2, {
fill: 'None',
stroke: STROKE_COLOR,
strokeWidth: 2
});
drawCircle(svg, [cx, cy], r3, {
fill: 'None',
stroke: STROKE_COLOR,
strokeWidth: 2
});

const N_TICKS = 24;
range(0, N_TICKS).forEach(function(i) {
const theta = (2 * Math.PI * i) / N_TICKS;
const [x1, y1] = [cx - Math.cos(theta) * r1, cy + Math.sin(theta) * r1];
const [x2, y2] = [cx - Math.cos(theta) * r2, cy + Math.sin(theta) * r2];
const strokeWidth = i % 3 === 0 ? 3 : 1;

drawLine(svg, [x1, y1], [x2, y2], {
stroke: STROKE_COLOR,
strokeWidth
});
});
}
});

return renderInfographic(options);
}
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