Published
Edited
Dec 5, 2020
1 fork
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
seriesA = (regen,
getRandomWalk(
n,
randWalkStart,
d3.randomNormal(randWalkMean, randWalkStDev)
))
Insert cell
seriesB = (regen,
getRandomWalk(
n,
randWalkStart + 0.1,
d3.randomNormal(randWalkMean, randWalkStDev)
))

Insert cell
xAccessor = d => d[0]
Insert cell
yAccessor = d => d[1]
Insert cell
Insert cell
series = [seriesA, seriesB].map(serie => serie.map(normalize))
Insert cell
productSeries = d3.zip(...series).map(([a, b]) => [a[0], a[1] * b[1]])
Insert cell
partialProduct = i =>
productSeries.slice(0, i + 1).concat(series[0].slice(i + 1))
Insert cell
data = d3.zip(...series).map(([a, b]) => ({
x: a[0],
a: a[1],
b: b[1],
A: [0, y(a[1]) - y(0)],
B: [0, y(b[1]) - y(0)]
}))
Insert cell
normalize = (d, i) => [xAccessor(d, i), yAccessor(d, i)]
Insert cell
Insert cell
xExtent = d3.extent(series.flatMap(s => s.map(d => d[0])))
Insert cell
yExtent = d3.extent([
0,
1,
...series.flatMap(s => s.map(d => d[1])),
...productSeries.map(d => d[1])
])
Insert cell
x = d3.scaleLinear(xExtent, [margin.left, width - margin.right])
Insert cell
y = d3.scaleLinear(yExtent, [height - margin.bottom, margin.top])
Insert cell
margin = ({ top: 10, right: 90, bottom: 20, left: 25 })
Insert cell
height = width / 2
Insert cell
Insert cell
xAxis = g => g.attr("transform", `translate(0, ${y(0)})`).call(d3.axisBottom(x))
Insert cell
yAxis = g =>
g.attr("transform", `translate(${x.range()[0]})`).call(d3.axisLeft(y))
Insert cell
dataLine = d3
.line()
.x((d, i) => x(d[0]))
.y((d, i) => y(d[1]))
Insert cell
line = d3
.line()
.x(d => d[0])
.y(d => d[1])
Insert cell
l = p => path => path.attr("d", line([o, p]))
Insert cell
pt = ([px, py]) => circle => circle.attr("cx", px).attr("cy", py)
Insert cell
o = [0, 0]
Insert cell
unity = [0, y(1) - y(0)]
Insert cell
Insert cell
renderPoint = function(d, i) {
const sel = d3.select(this);

d.l1 = sel
.append("path")
.attr("stroke", girderColor)
.call(l(unity));
d.pt1 = sel
.append("circle")
.attr("fill", girderColor)
.attr("r", ptR)
.call(pt(unity));

d.l1A = sel
.append("path")
.attr("stroke", girderColor)
.attr("d", line([d.A, unity]));
d.lAB = sel
.append("path")
.attr("stroke", girderColor)
.call(l(d.A));
d.ptAB = sel
.append("circle")
.attr("fill", girderColor)
.attr("r", ptR)
.call(pt(d.A));

d.lA = sel
.append("path")
.attr("stroke", color(null, 0))
.call(l(d.A));
d.lB = sel
.append("path")
.attr("stroke", color(null, 1))
.call(l(d.B));
d.ptA = sel
.append("circle")
.attr("fill", color(null, 0))
.attr("r", ptR)
.call(pt(d.A));
d.ptB = sel
.append("circle")
.attr("fill", color(null, 1))
.attr("r", ptR)
.call(pt(d.B));
}
Insert cell
color = (d, i) => (i ? "steelblue" : "orange")
Insert cell
girderColor = "#ccc"
Insert cell
duration = speed
Insert cell
delay = speed
Insert cell
Insert cell
// https://stackoverflow.com/a/2259502/120290
rotate = ([px, py], angle, [cx, cy] = [0, 0]) => {
const s = Math.sin(angle);
const c = Math.cos(angle);

// translate point back to origin
px -= cx;
py -= cy;

// rotate point
const xnew = px * c - py * s;
const ynew = px * s + py * c;

// translate point back
px = xnew + cx;
py = ynew + cy;
return [px, py];
}
Insert cell
multiply = ([px, py], coef) => [coef * px, coef * py]
Insert cell
Insert cell
d3 = require("d3")
Insert cell
import { getRandomWalk } from "@tophtucker/scrapbook"
Insert cell
import { slider } from "@jashkenas/inputs"
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