Public
Edited
Oct 1, 2023
Insert cell
Insert cell
Insert cell
Insert cell
import { range, factorial } from "@chiahsun-ws/math-functions"
Insert cell
// https://stackoverflow.com/questions/3746725/how-to-create-an-array-containing-1-n
d3.map(Array.from(Array(10).keys()), n => factorial(n))
Insert cell
// Use the mathematical formula derived from taylor series expansion for cos(x) at x = 0
function getExpansionFn(N, maxVal = 2000000000) {
function f(x) {
let result = 0;
for (let i = 0; i <= N; ++i) {
const num = x ** (2 * i);
const denom = factorial(2*i);
// if (num > maxVal || denom > maxVal) return null;
result += (-1) ** i * num / denom;
}
return result;
}
return f;
}
Insert cell
// 0.01
step = (domainX[1] - domainX[0]) / 10000
Insert cell
getData = () => [
d3.map(range(domainX[0], domainX[1], step), (d) => ({x: d, y: Math.cos(d), f: "cos"})),
d3.map(range(domainX[0], domainX[1], step), (d) => ({x: d, y: getExpansionFn(firstN)(d), f: `Taylor series expansion for first ${firstN} terms`}))
].flat().filter(d => d.y !== null)
Insert cell
mutable data = getData()
Insert cell
d3.extent(data, d => d.y)
Insert cell
Insert cell
viewof firstN = Inputs.range([1, 20], {label: "Get first k order terms", step: 1})
Insert cell
viewof resetButton = Inputs.button("Reset")
Insert cell
viewof dragR = Inputs.range([3.4, 4], {label: "Amount", step: 0.001, value: 3.81})
Insert cell
chart = Plot.plot({
width: 900,
color: { legend: true },
x: {grid: true, domain: domainX},
y: {grid: true, domain: domainY},
marks:[
Plot.lineY(data, {x: "x", y: "y", stroke: "f"})
]
})
Insert cell
defaultDomainX = [-20, 20]
Insert cell
mutable domainX = defaultDomainX
Insert cell
defaultDomainY = [-2, 2]
Insert cell
mutable domainY = defaultDomainY
Insert cell
{
const zoom = d3.zoom()
// .filter((event) => event.type === "wheel")
// .filter((event) => !event.button)
// .filter((event) => event.type !== "dblclick" && event.type !== "click")
// .filter((event) => event.type === "zoom")
.on("zoom", function ({transform, sourceEvent}) {
const scaleX = chart.scale("x");
const x = d3.scaleLinear().domain(scaleX.domain).range(scaleX.range);
const rescaledX = transform.rescaleX(x).interpolate(d3.interpolateRound);
mutable domainX = d3.extent(rescaledX.domain());


const scaleY = chart.scale("y");
const y = d3.scaleLinear().domain(scaleY.domain).range(scaleY.range);
const rescaledY = transform.rescaleY(y).interpolate(d3.interpolateRound);
// Something wrong(bug?) for rescale Y, it behaves different for pan/zoom events so for one of them we need to use a workaround
// https://stackoverflow.com/questions/58066985/how-can-i-detect-panning-and-zooming-by-the-user-with-d3-zoombehavior
// https://github.com/d3/d3-zoom#zoom-events
if(sourceEvent.type === "wheel") {
mutable domainY = d3.extent(rescaledY.domain());
} else if (sourceEvent.type === "mousemove") { // Still some glitches in it ...
const d = rescaledY.domain()[1] - rescaledY.domain()[0];
mutable domainY = [rescaledY.domain()[0] - d * dragR, rescaledY.domain()[1] - d * dragR];
}
})

function initZoom() {
d3.select(chart).call(zoom);
}

initZoom();
}
Insert cell
function reset() {
mutable domainX = defaultDomainX;
mutable domainY = defaultDomainY;
}
Insert cell
resetButton, reset()
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