Public
Edited
Mar 4
10 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
MIN = 0
Insert cell
MAX = 11
Insert cell
DELTA = MAX - MIN
Insert cell
Insert cell
interpolators = Array(DELTA)
.fill()
.map((_, i) => {
const from = i + MIN;
return d3.interpolate(
// To interpolate between n and n+1, double the points in n
doubleArray(sierpinskiCurve(size, from).points),
sierpinskiCurve(size, from + 1).points
);
})
Insert cell
Insert cell
Insert cell
timeUpdater = {
const elapsed = playing ? now - startTime : 0;
if (elapsedTime !== elapsed) {
mutable elapsedTime = elapsed;
}
}
Insert cell
mutable elapsedTime = 0
Insert cell
Insert cell
Insert cell
Insert cell
playhead = Math.abs(
((elapsedTime + TIME_TOTAL) % (TIME_TOTAL * 2)) - TIME_TOTAL
) // Linear interpolation back and forth (saw wave) from 0 to TIME_TOTAL
Insert cell
goingUp = !playing || Math.floor(elapsedTime / TIME_TOTAL) % 2 === 0
// It took a couple of days to think how to do this in a stateless way
Insert cell
interp = playing ? DELTA * (playhead / TIME_TOTAL) : sliderValue - MIN
Insert cell
interpIndex = Math.floor(interp)
Insert cell
interpRemainder = interp % 1
Insert cell
smoothRemainder = Math.sin(interpRemainder * Math.PI + Math.PI / 2) / -2 + 0.5
Insert cell
points = {
if (!playing) {
let index = interpIndex;
let remainder = interpRemainder;
if (index >= interpolators.length) {
index = interpolators.length - 1;
remainder = 1;
}
return yield interpolators[index](remainder);
}

while (playing) {
yield interpolators[interpIndex](smoothRemainder);
}
}
Insert cell
pathEl = d3.select(animatedSVG).select('path#curve')
Insert cell
{
// So we don't remake the svg on every frame of animation,
// this cell will update the path's d attribute when the
// points array changes
pathEl.attr('d', lineGen(points) + "Z");
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
triangles = Array(DELTA + 1)
.fill()
.map((_, i) => {
const from = i + MIN;
return sierpinskiCurve(size, from).triangles.reduce((d, tri) => {
return (
d +
`M ${tri[0][0]},${tri[0][1]} L ${tri[1][0]},${tri[1][1]} L ${tri[2][0]},${tri[2][1]} Z `
);
}, '');
})
Insert cell
triIndexUpdater = {
// Make grid match the target iteration of the curve animation
let index = goingUp ? Math.ceil(interp) : Math.floor(interp);
if (index >= triangles.length) {
index = triangles.length - 1;
}
if (triIndex !== index) {
mutable triIndex = index;
}
}
Insert cell
mutable triIndex = 0
Insert cell
gridPathEl = d3.select(animatedSVG).select('path#grid')
Insert cell
{
const path = triangles[triIndex];
gridPathEl.attr('d', path);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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