Public
Edited
Jun 30, 2023
Insert cell
Insert cell
viewof offset = Inputs.range([-20, 20], { value: 5, step: 1, label: "Offset" })
Insert cell
viewof hueRandomNess = Inputs.range([-20, 20], {
value: 5,
step: 1,
label: "Hue randomness"
})
Insert cell
initialLine = [
[100, 100],
[120, 120]
]
Insert cell
viewof nbPoints = Inputs.range([0, 200], {
value: 5,
step: 1,
label: "Points"
})
Insert cell
chart = {
const svg = d3.select(DOM.svg(400, 400));

const line = d3.line();

for (let i = 0; i < nbPoints; i++) {
svg
.selectAll("path")
.data(data.slice(0, i))
.enter()
.append("path")
.attr("d", (d) => line(d.line))
.attr("stroke", (d) => d.color)
.attr("stroke-opacity", 0.5)
.attr("stroke-width", 11);
yield svg.node();
}

return svg.node();
}
Insert cell
offsetColor = (color, offset) => {
const c = color.copy();
c.h += offset * Math.random() * hueRandomNess;
c.s += Math.random() * 0.1;
return c;
}
Insert cell
normalOffset = (segment, offset) => {
const [[x0, y0], [x1, y1]] = segment;
const [dx, dy] = [[x1 - x0], [y1 - y0]];
const length = Math.sqrt(dx * dx + dy * dy);
const u = [dx / length, dy / length];
const v = [-u[0], u[1]];

return [
[x0 + v[0] * offset, y0 + v[1] * offset],
[x1 + v[0] * offset, y1 + v[1] * offset]
];
}
Insert cell
data = {
const color = d3.hsl("steelblue");
const data = [{ line: initialLine, color: color }];
for (let i = -Math.floor(nbPoints / 2); i < Math.ceil(nbPoints / 2); i++) {
if (i == 0) {
continue;
}

const transformSegment = (segment, offset, rotation) => {
segment = rotateSegment(segment, rotation, segment[0]);
segment = normalOffset(segment, offset);
return segment;
};
data.push({
line: transformSegment(data[0].line, i * 5, i / 20),
color: offsetColor(color, i * 10)
});
}
return data;
}
Insert cell
function rotateSegment(segment, angle, rotationPoint) {
const cosAngle = Math.cos(angle);
const sinAngle = Math.sin(angle);

const [startX, startY] = segment[0];
const [endX, endY] = segment[1];
const [rotationX, rotationY] = rotationPoint;

const translatedStart = [startX - rotationX, startY - rotationY];
const rotatedStart = [
translatedStart[0] * cosAngle - translatedStart[1] * sinAngle,
translatedStart[0] * sinAngle + translatedStart[1] * cosAngle
];

const translatedEnd = [endX - rotationX, endY - rotationY];
const rotatedEnd = [
translatedEnd[0] * cosAngle - translatedEnd[1] * sinAngle,
translatedEnd[0] * sinAngle + translatedEnd[1] * cosAngle
];

return [
[rotatedStart[0] + rotationX, rotatedStart[1] + rotationY],
[rotatedEnd[0] + rotationX, rotatedEnd[1] + rotationY]
];
}
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