Public
Edited
May 4, 2023
Insert cell
Insert cell
{
const viewWidth = 696;
// const viewWidth=width
const maxWidth = viewWidth;
const viewHeight = 396;
// const viewHeight=520
const margin = { top: 48, bottom: 60, left: 12, right: 12 };

return state.doses;
const caffeineStreams = dosesToCaffeineStreams(state.doses, {
totalHours,
caffeinePerCupMg
}).sort((l, r) => l[0][0] - r[0][0]);

return caffeineStreams;

const stackedStreams = stackStreams(caffeineStreams);
let streamsToPlot = stackedStreams.map((curStream) => {
return curStream
.filter(([x]) => x <= 30)
.map(([x, y0, y]) => ({ x, y0, y }));
});

// Assign IDs to the streams
let streamCountByTime = new Map();
streamsToPlot.forEach((curStream) => {
const countForTime = streamCountByTime.get(curStream[0].x) || 0;
// Update streamCountByTime
streamCountByTime.set(curStream[0].x, countForTime + 1);
// Set an ID on the current stream
curStream.id = `${curStream[0].x}_${countForTime}`;
});

const streamIDs = new Set(streamsToPlot.map((ds) => ds.id));
const enteredStreamIDs = new Set(
[...streamIDs].filter((x) => !visibleStreamIDs.has(x))
);
const exitedStreamIDs = new Set(
[...visibleStreamIDs].filter((x) => !streamIDs.has(x))
);

if (0 !== enteredStreamIDs.size || 0 !== exitedStreamIDs.size) {
// mutable visibleDataHashes = dataHashes
console.log("Entered: " + Array.from(enteredStreamIDs));
console.log("Exited: " + Array.from(exitedStreamIDs));
new Promise(async () => {
await Promises.delay(500);
mutable visibleStreamIDs = new Set(
[...visibleStreamIDs, ...enteredStreamIDs].filter(
(x) => !exitedStreamIDs.has(x)
)
);
});
}

// Mark new streams as entered
// This is not great and super fragile
streamsToPlot.forEach((curStream) => {
curStream.entered = enteredStreamIDs.has(curStream.id);
curStream.exited = exitedStreamIDs.has(curStream.id);
});

// return streamsToPlot

const maxCaffeine = d3.max(
streamsToPlot.flat().map((d) => caffeineSleepThreshold <= d.y)
);
const caffeineBelowSleepThreshold = maxCaffeine <= caffeineSleepThreshold;

const sleepTime = calculateSleepTime(streamsToPlot);
const description = getDescriptionText(streamsToPlot);
const descriptionParts = description.split("\n");
const colors = d3.quantize(
d3.interpolateRgb("#E6762A", "#F3E500"),
Math.max(2, streamsToPlot.length)
);
// const grid = renderGrid()
const grid = null;

const renderedCaffeineChart = caffeineChart(streamsToPlot, {
colors,
width: viewWidth - margin.left - margin.right,
height: viewHeight - margin.top - margin.bottom,
domain: [6, 30]
});

return svg`
<svg width="${Math.min(maxWidth, width)}"
height="${viewHeight * (Math.min(maxWidth, width) / viewWidth)}"
viewBox="0 0 ${viewWidth} ${viewHeight}"
style="max-width: ${maxWidth}px;">

<style>
text {
fill: #1B1A4B;
/* font-family: "Helvetica Neue", helvetica, arial, san-serif;*/
}
.title-text {
font-size: 1.5em;
font-weight: bold;
}

.description-text {
font-size: 0.9em;
}

.title-text text,
.description-text text {
alignment-baseline: bottom;
}

.background-rect {
fill: #FEFCF9;

}

.y-axis > path {
display: none;
}
.y-axis text {
alignment-baseline: bottom;

}
.x-axis > path {
display: none;
/*stroke-width: 3px;
stroke-color: #1B1A4B;*/
}
.tick line {
display: none;
}

.sleep-line {
fill: #238BCD;
}

.sleep-line-border {
stroke: #FFFDF7;
stroke-width: 7;
}

@keyframes fadein {
from { opacity: 0.2; }
to { opacity: 1; }
}

.stream.entered {
animation: fadein 0.5s;
}

</style>

<rect class="background-rect" width="${viewWidth}" height="${viewHeight}"/>

<g transform="translate(${margin.left + 48}, ${margin.top})">
<text>
<tspan class="title-text" x="0">Caffeine Levels by Hour</tspan>
<tspan class="description-text" x="0" dy="24">
${descriptionParts[0]}
</tspan>
<tspan class="description-text" x="0" dy="24">
${descriptionParts[1] || ""}
</tspan>

</text>
</g>

<g transform="translate(${margin.left}, ${margin.top + 72})">
${renderedCaffeineChart}
</g>
${grid}

</svg>
`;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
//This is kindof a stupid design
upsample= (data, keys, multiple=10) => {
const interpolators = keys.map(k=>(data.map(d=>d[k])))
.map(x=>(d3.piecewise(d3.interpolateNumber, x)))
// .map(d3.interpolateBasis)
const n = data.length * multiple
const upsampledData = interpolators
.map(interpolator=> d3.quantize(interpolator, n))
return d3.range(n).map(i=>{
return keys.reduce((agg,k,j)=>({...agg, [k]: upsampledData[j][i]}),{})
})
}
Insert cell
Insert cell
dosesToCaffeineStreams = (dosesByHour, {totalHours, caffeinePerCupMg}) => {
return Array.from(dosesByHour).map(([startTime, cups])=>{
startTime = parseInt(startTime)
return d3.range(cups).map(i=>{
const dose = caffeinePerCupMg
return [[startTime, 0],
...d3.range(startTime+1,totalHours)
.map(t=>([t,1.0 * dose * caffeineRemaining(t-startTime)]))]})
}).flat()

}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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