Published
Edited
Dec 12, 2021
4 stars
Insert cell
Insert cell
Spline = (await import("https://cdn.skypack.dev/cubic-spline@3.0.3?min"))
.default
Insert cell
mutable data = [[1,9], [2,3], [3,6], [4,2], [5,4]]
Insert cell
spline = new Spline(
data.map((d) => d[0]),
data.map((d) => d[1])
)
Insert cell
interpolate = (t) => spline.at(t)
Insert cell
Insert cell
Insert cell
[t, interpolate(t)]
Insert cell
Insert cell
{
const svg = d3.select(DOM.svg(500, 500));

svg
.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 10)
.attr("d", line(data));

svg
.append("polyline")
.attr("fill", "none")
.attr("stroke", "blue")
.attr("stroke-width", 3)
.attr(
"points",
interpPts.map((d) => `${scaleX(d[0])},${scaleY(d[1])} `).join("")
);

svg
.append("circle")
.attr("cx", scaleX(t))
.attr("cy", scaleY(interpolate(t)))
.attr("fill", "blue")
.attr("r", 15);

const circles = svg
.append("g")
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", (d) => scaleX(d[0]))
.attr("cy", (d) => scaleY(d[1]))
.attr("r", 10)
.attr("fill", "red")
.attr("cursor", "grab")
.call(
d3.drag().subject(dragSubject).on("drag", dragged).on("end", dragEnd)
);

function dragSubject() {
const { x, y } = d3.event;
return {
dx: x - d3.select(this).attr("cx"),
dy: y - d3.select(this).attr("cy")
};
}
function dragged() {
const {
x,
y,
subject: { dx, dy }
} = d3.event;
const cx = Math.min(Math.max(x - dx, 0), 500);
const cy = Math.min(Math.max(y - dy, 0), 500);
d3.select(this).attr("cx", cx).attr("cy", cy).attr("cursor", "grabbing");
}
function dragEnd(d, i) {
const cx = d3.select(this).attr("cx");
const cy = d3.select(this).attr("cy");
const newdata = data.slice();
newdata.splice(i, 1, [scaleX.invert(cx), scaleY.invert(cy)]);
newdata.sort((a, b) => a[0] - b[0]);
mutable data = newdata;
circles.attr("cursor", "grab");
}

return svg.node();
}
Insert cell
Insert cell
interpPts = Array.from({length:100}, (_,i) => {
const x = (1-i/99)*data[0][0] + (i/99)*data[data.length-1][0];
return [x, interpolate(x)];
})
Insert cell
line = d3.line()
.x(d => scaleX(d[0]))
.y(d => scaleY(d[1]))
.curve(d3.curveNatural)
Insert cell
scaleX = d3.scaleLinear()
.domain([1,5])
.range([15,500-15])
Insert cell
scaleY = d3.scaleLinear()
.domain([4,9])
.range([500-170,15])
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